[SDOI2018]战略游戏
Posted zh-comld
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2018]战略游戏相关的知识,希望对你有一定的参考价值。
题目描述
省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏。
这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到
任意其他城市。现在小C已经占领了其中至少两个城市,小Q可以摧毁一个小C没占领的城市,同时摧毁所有连接这
个城市的道路。只要在摧毁这个城市之后能够找到某两个小C占领的城市u和v,使得从u出发沿着道路无论如何都不
能走到v,那么小Q就能赢下这一局游戏。
小Q和小C一共进行了q局游戏,每一局游戏会给出小C占领的城市集合S
你需要帮小Q数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。
题解
如果这是一棵树,那么做法就是直接对询问点见虚树,统计包含树中的点的最小联通块的大小。
现在它是一张无向图,那么就把圆方树建出来,考虑一条路径经过了圆->方->圆点,那么那两个圆点割点,应对答案有1的贡献,方点的贡献为0。
代码
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<algorithm> #define N 200009 using namespace std; typedef long long ll; int dfn[N],head[N],tot,dis[N],n,low[N],ans,num,st[N],top,rbs[N],a[N]; bool vis[N]; inline ll rd(){ ll x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } struct edge{int n,to;}e[N<<1]; inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;} vector<int>vec[N],ed[N]; inline bool cmp(int a,int b){return dfn[a]<dfn[b];} struct LCA{ int top[N],size[N],son[N],fa[N],deep[N]; void clear(){memset(deep,0,sizeof(deep));memset(top,0,sizeof(top));memset(son,0,sizeof(son));memset(fa,0,sizeof(fa));} void dfs(int u){ size[u]=1;dis[u]=dis[fa[u]]+(u<=n); for(int i=0;i<vec[u].size();++i){ int v=vec[u][i]; fa[v]=u;deep[v]=deep[u]+1; dfs(v);size[u]+=size[v]; if(size[v]>size[son[u]])son[u]=v; } } void dfs2(int u){ dfn[u]=++dfn[0];if(!top[u])top[u]=u; if(son[u])top[son[u]]=top[u],dfs2(son[u]); for(int i=0;i<vec[u].size();++i){ int v=vec[u][i]; if(v!=fa[u]&&v!=son[u])dfs2(v); } } inline int getlca(int u,int v){ while(top[u]!=top[v]){ if(deep[top[u]]<deep[top[v]])swap(u,v); u=fa[top[u]]; } return deep[u]<deep[v]?u:v; } }lca; void tarjan(int u,int fa){ dfn[u]=low[u]=++dfn[0];vis[u]=1;st[++top]=u; for(int i=head[u];i;i=e[i].n)if((i^fa)!=1){ int v=e[i].to;if(dfn[v]>dfn[u])continue; if(!dfn[v]){ tarjan(v,i); low[u]=min(low[u],low[v]); } else if(vis[v])low[u]=min(low[u],dfn[v]); if(dfn[u]==low[v]){ ++num;vec[u].push_back(num);//cout<<u<<" "<<num<<endl; while(st[top]!=v){ vec[num].push_back(st[top]);//cout<<num<<" "<<st[top]<<endl; vis[st[top]]=0;top--; } vec[num].push_back(v);vis[v]=0;top--;//cout<<num<<" "<<v<<endl; } if(low[v]>dfn[u]){ vec[u].push_back(v); top--;vis[v]=0; } } } void dfs(int u){ for(int i=0;i<ed[u].size();++i){ int v=ed[u][i]; dfs(v); ans+=dis[v]-dis[u]; if(vis[v]&&v<=n)ans--; } } inline void init(){ tot=1;top=0; memset(head,0,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(dis,0,sizeof(dis)); lca.clear(); } inline void solve(){ n=rd();int m=rd();int u,v; init(); for(int i=1;i<=m;++i){ u=rd();v=rd(); add(u,v);add(v,u); } num=n; tarjan(1,0); memset(dfn,0,sizeof(dfn)); top=0; lca.dfs(1);lca.dfs2(1); memset(vis,0,sizeof(vis)); int q=rd(); while(q--){ int s=rd();ans=0; for(int i=1;i<=s;++i)a[i]=rd(),vis[a[i]]=1; sort(a+1,a+s+1,cmp); st[top=1]=rbs[rbs[0]=1]=a[1];int root=a[1]; for(int i=2;i<=s;++i){ int x=a[i];//cout<<x<<"guiu"<<endl; int l=lca.getlca(x,st[top]); if(l==st[top]){st[++top]=x;rbs[++rbs[0]]=x;continue;} while(top>1){ int xx=st[top],yy=st[top-1]; if(dfn[yy]<=dfn[l]){ ed[l].push_back(xx);//cout<<l<<" "<<xx<<endl; top--;break; } ed[yy].push_back(xx);top--;//cout<<yy<<" "<<xx<<endl; } if(dfn[l]<dfn[st[top]]){ if(dfn[l]<dfn[root])root=l; // cout<<l<<" "<<st[top]<<endl; ed[l].push_back(st[top]);top--; } if(l!=st[top])st[++top]=l,rbs[++rbs[0]]=l; st[++top]=x;rbs[++rbs[0]]=x; } while(top>1){ // cout<<st[top-1]<<" "<<st[top]<<endl; ed[st[top-1]].push_back(st[top]); top--; } if(!vis[root]&&root<=n)ans++; dfs(root); printf("%d ",ans); while(rbs[0]){ int x=rbs[rbs[0]]; vis[x]=0;ed[x].clear();rbs[0]--; } } for(int i=1;i<=num;++i)vec[i].clear(); } int main(){ int T=rd(); while(T--)solve(); return 0; }
这里的圆方树不能随便的建,只能对每个点双开一个。、
具体建法为:对于非点双内的边,直接连,并且弹栈。
if(low[v]>dfn[u]){ vec[u].push_back(v); top--;vis[v]=0; }
对于每个割顶,新开一个节点,然后边弹栈边连边。
if(dfn[u]==low[v]){ ++num;vec[u].push_back(num); while(st[top]!=v){ vec[num].push_back(st[top]); vis[st[top]]=0;top--; } vec[num].push_back(v);vis[v]=0;top--; }
而且我们对于每个点双都只能算一次,为了避免算重,我们需要特判一下。
if(dfn[v]>dfn[u])continue
以上是关于[SDOI2018]战略游戏的主要内容,如果未能解决你的问题,请参考以下文章