P4606 [SDOI2018]战略游戏:广义圆方树

其实会了圆方树就不难,达不到黑,最多算个紫

那个转换到圆方树上以后的处理方法,画画图就能看出来,所以做图论题一定要多画图,并把图画清楚点啊!!

但我怎么从9点一直调到下午4点啊啊啊啊啊

双倍经验:P4320 道路相遇,但是被卡常了并没有过

给出一个无向图,和 \(q\) 个询问,每次给出 \(s\) 个点,问存在几个点,使得这个点和他相连的边被去除后,这 \(s\) 个点中,至少存在一对点互不相通


先用 tarjan 对原图建立圆方树,然后可以枚举每两对给定的点,求他们的树上路径中,有几个非叶子节点的圆点(根据圆方树的性质,这就是他们之间的割点,也就是满足题目要求的点)

这些点的并集的大小即为答案

但这样复杂度显然过不去,而且这样描述答案不方便做转化(一开始我就是一直在这样想,导致卡了好长时间)

所以应该给出另一种对答案的描述:在圆方树中,包含所有给出的点的联通块,最小的大小,再减去 \(s\) 即为答案

显然这和刚才那样是等价的

但是还是不会算

发现题目中给出的 \(s\) 个点是排好序的,然而这并没有什么用,但这启示我们,不妨把给出的点按照他们在圆方树中的 dfs 序重新排序

然后画一个图,发现,如果由 dfs 序从小到大,以此走过所有的点,然后再从第 \(s\) 个点走回第 \(1\) 个点

在走过路径中,如果不考虑每相邻两个点的 LCA(此时我们走的是树上最短路径,显然会经过 LCA,这里说的不考虑就是不把它计入在内),每个点恰好被走了两次,而这些被走过的点恰好就是我们要求的联通块

其实这里本来是有个图的,但画的太丑就没加进来,自己画一画吧

所以我们只要为每个点赋值一个点权,方点为 \(0\) 圆点为 \(1\),倍增求树上路径点权和就好了

当然,最后还要再除以二,再减去 \(s\)

不过这样会有一个问题,就是第一个点和第 \(s\) 个点的 LCA 会不被统计,所以如果这个点是个圆点答案就再加一

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define N 400007
#define M 400006
struct graph{
int tot;
int fir[N],nex[M],to[M];
inline void add(int u,int v){
to[++tot]=v;
nex[tot]=fir[u];fir[u]=tot;
}
inline void clear(){
std::memset(fir,0,sizeof fir);std::memset(to,0,sizeof to);
std::memset(nex,0,sizeof nex);tot=0;
}
}T,G;
int n,m;
int dfn[N],deep[N],size[N],dfscnt;
int fa[22][N],val[N],S[N];
inline int cmp(int x,int y){return dfn[x]<dfn[y];}
struct get_bcc{
int dfn[N],low[N],stack[N],top;
int bcccnt,dfscnt;
void tarjan(int u){
dfn[u]=low[u]=++dfscnt;stack[top++]=u;
for(reg int v,i=G.fir[u];i;i=G.nex[i]){
v=G.to[i];
if(!dfn[v]){
tarjan(v);
low[u]=std::min(low[u],low[v]);
if(low[v]>=dfn[u]){
bcccnt++;
do{
T.add(bcccnt,stack[--top]);T.add(stack[top],bcccnt);
}while(stack[top]^v);
T.add(bcccnt,u);T.add(u,bcccnt);
}
}
else low[u]=std::min(low[u],dfn[v]);
}
}
}BCC;
inline void clear(){
T.clear();G.clear();
std::memset(BCC.dfn,0,sizeof BCC.dfn);std::memset(BCC.low,0,sizeof BCC.low);
BCC.top=0;BCC.dfscnt=0;
dfscnt=0;
std::memset(dfn,0,sizeof dfn);
std::memset(fa,0,sizeof fa);std::memset(val,0,sizeof val);
std::memset(deep,0,sizeof deep);std::memset(size,0,sizeof size);
}
void dfs(int u,int fat){
size[u]=1;deep[u]=deep[fat]+1;
dfn[u]=++dfscnt;fa[0][u]=fat;
val[u]=val[fat]+(u<=n);
for(reg int v,i=T.fir[u];i;i=T.nex[i]){
v=T.to[i];
if(v==fat) continue;
dfs(v,u);size[u]+=size[v];
}
}
inline void pre(){
for(reg int i=1;i<=20;i++){
for(reg int j=1;j<=BCC.bcccnt;j++) fa[i][j]=fa[i-1][fa[i-1][j]];
}
}
inline int get(int x,int y){
if(deep[x]<deep[y]) std::swap(x,y);
for(reg int i=20;~i;i--)
if(deep[fa[i][x]]>=deep[y])x=fa[i][x];
if(x==y) return x;
for(reg int i=20;~i;i--)
if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
return fa[0][x];
}
int main(){int cases=read();while(cases--){
BCC.bcccnt=n=read();m=read();
for(reg int u,v,i=1;i<=m;i++){
u=read();v=read();
G.add(u,v);G.add(v,u);
}
BCC.tarjan(1);
dfs(1,0);
pre();
//
// EN;EN;
// std::printf("n : %d m : %d bcccnt : %d\n",n,m,BCC.bcccnt);
// for(reg int i=1;i<=BCC.bcccnt;i++){
// std::printf("now : %d size : %d deep : %d dfn : %d val : %d\n",i,size[i],deep[i],dfn[i],val[0][i]);
// for(reg int j=T.fir[i];j;j=T.nex[j]) std::printf("%d ",T.to[j]);
// EN;
// }
//
reg int s,ans,lca;
int q=read();while(q--){
s=read();ans=0;
for(reg int i=1;i<=s;i++) S[i]=read();
std::sort(S+1,S+1+s,cmp);S[s+1]=S[1];
for(reg int i=1;i<=s;i++){
lca=get(S[i],S[i+1]);
ans+=val[S[i]]+val[S[i+1]]-val[lca]*2;
}
ans>>=1;ans-=s;
ans+=(get(S[1],S[s])<=n);
std::printf("%d\n",ans);
}
if(cases) clear();
}
return 0;
}

[bzoj5329] P4606 [SDOI2018]战略游戏的更多相关文章

  1. luogu P4606 [SDOI2018]战略游戏

    LINK:战略游戏 一道很有价值的题目.这道题 一张无向联通图 每次询问给出K个关键点 问摧毁图中哪个点可以使得这K个关键的两两之间有一对不能联通 去掉的这个点不能是关键点 求方案数. 可以发现 当K ...

  2. Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树

    https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...

  3. 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】

    题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...

  4. 洛谷P4606 [SDOI2018]战略游戏 [广义圆方树]

    传送门 思路 先考虑两点如何使他们不连通. 显然路径上所有的割点都满足条件. 多个点呢?也是这样的. 于是可以想到圆方树.一个点集的答案就是它的虚树里圆点个数减去点集大小. 可以把点按dfs序排序,然 ...

  5. [SDOI2018]战略游戏 圆方树,树链剖分

    [SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中 ...

  6. bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树)

    bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树) bzoj Luogu 题目描述略(太长了) 题解时间 切掉一个点,连通性变化. 上圆方树. $ \sum |S| ...

  7. BZOJ5329: [SDOI2018]战略游戏——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=5329 https://www.luogu.org/problemnew/show/P4606 省选 ...

  8. BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)

    Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...

  9. [BZOJ5329][SDOI2018]战略游戏

    bzoj luogu Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任 ...

随机推荐

  1. 计算机网络协议,PPP协议分析

    一.基本特点 1.PPP协议是计算机网络体系中第二层(数据链路层)的协议 2.PPP帧格式是以HDLC帧格式为基础,做了很少的改动(区别:PPP是面向字符的,而HDLC是面向位的) 3.PPP协议使用 ...

  2. web自动化测试中的PO模式(一)

    1.PO模式的思想 原理: 将页面的元素定位和元素行为封装成一个page类 类的属性:元素的定位 类的行为:元素的操作 页面对象和测试用例分离 测试用例: 调用所需要页面对象中的行为,组成测试用例 测 ...

  3. Java异常处理总结Exception\Error

    Java异常处理总结Exception\Error 2012-12-28 08:17:17|  分类: JAVA |  标签:java  |举报|字号 订阅   Java异常处理总结          ...

  4. Java面向对象的总结

    面向对象的程序设计 1.面向对象 核心:以类的方式组织代码,以对象的方式封装数据 比喻:也就是说类是没有数据的,给了数据之后的类就是对象 封装 继承 多态 2.方法 a.一个方法只有一个返回值,只有一 ...

  5. C#多线程(6):线程通知

    目录 AutoRestEvent 类 常用方法 一个简单的示例 解释一下 复杂一点的示例 解释 回顾一下,前面 lock.Monitor 部分我们学习了线程锁,Mutex 部分学习了进程同步,Sema ...

  6. Java中常用的七个阻塞队列介绍第一篇

    Java中常用的七个阻塞队列介绍第一篇 在上一篇我们对Java中的队列分类做了简单的介绍.本文咱们主要来聊聊阻塞队列中的七个常用子类.这七个阻塞队列的学习步骤:先看源码,分析完源码之后,我们再来对每个 ...

  7. 1个工具,助你提升K8S故障排查效率!

    Kubernetes的故障排查一直困扰众多运维团队或DevOps,除了Kubernetes本身的复杂性之外,还有Kubernetes的工作负载是动态的原因.本文将介绍1个工具可以帮助你可视化K8S的网 ...

  8. B2 - TV Subscriptions (Hard Version)

    题目连接:https://codeforces.com/contest/1247/problem/B2 题解:双指针,,一个头,一个尾,头部进入,尾部退出,一开始先记录1到k,并记录每个数字出现的次数 ...

  9. tortoise 设置beyond Compare比较工具

    1.桌面右击tortoiseSVN->setting->Diff Viewer面板,选择external,选中beyond Compare路径

  10. 4.JS跳转路由/刷新/返回页面

    1.JS跳转路由(需要拿到父组件的history) clickHandle(){ let history = this.props.history; history.push( '/home') } ...