战略游戏(game)——圆方树&虚树

题目描述
省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏。
这款战略游戏的地图由
n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到任意其他城市。现在小C已经占领了其中至少两个城市,小Q可以摧毁一个小C没占领的城市,同时摧毁所有连接这个城市的道路。只要在摧毁这个城市之后能够找到某两个小C占领的城市u和v,使得从u出发沿着道路无论如何都不能走到v,那么小Q就能赢下这一局游戏。
小Q和小C一共进行了q局游戏,每一局游戏会给出小C占领的城市集合S,你需要帮小Q数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。

输入
第一行包含一个正整数T,表示测试数据的组数,
对于每组测试数据,
第一行是两个整数
n和m,表示地图的城市数和道路数,
接下来m行,每行包含两个整数u和v(1≤u,v≤n),表示第u个城市和第v个城市之间有一条道路,同一对城市之间可能有多条道路连接,
第m+1是一个整数q,表示游戏的局数,
接下来q行,每行先给出一个整数|S|(2≤|S|≤n),表示小C占领的城市数量,然后给出|S|个整数s1,s2,…,s|S|(1≤s1,s2<⋯,<,s|S|≤n),表示小C占领的城市。
输出
对于每一局游戏,输出一行,包含一个整数,表示这一局游戏中有多少个城市在小Q摧毁之后能够让他赢下这一局游戏。

样例输入
2
7 6
1 2
1 3
2 4
2 5
3 6
3 7
3
2 1 2
3 2 3 4
4 4 5 6 7
6 6
1 2
1 3
2 3
1 4
2 5
3 6
4
3 1 2 3
3 1 2 6
3 1 5 6
3 4 5 6
样例输出
0
1
3
0
1
2
3
提示
对于30%的分数,对于每组测试数据,有∑|S|≤20。
对于另外45%的分数,对于每一次询问,满足|S|=2。
对于100%的分数,1≤T≤10,2≤n≤10^5且n−1≤m≤2×10^5,1≤q≤10^5,且对于每组测试数据,有∑|S|≤2×10^5。

题解
这是一道辣鸡图论题,蒟蒻因为fa[sta[top]]=sta[top-1],top–;写成fa[sta[top]]=sta[–top]调了两个小时,感受到了细节的可怕
这道题是求删去某些点之后u,v不连通的方案数
因为是删点求不连通,我们便想到用Tarjan的点双来解决
对于图中的每一个点双连通块,新建一个方点,和每个原来的点连接,对于不同点双连通快中的点按照原图连接,这样子就构成一颗新的圆方树
这里写图片描述
这里写图片描述
对于这颗圆方树,ans即为(u,v)中圆点的个数
统计答案时,可以将每一个u,v和它们的lca拎出来,重新构成一棵树,然后统计树上圆点的个数减去S(小Q占领的城市个数)即可

虚树处理
1.输入每个询问的点,并且按照dfs序为关键字排序
2.将第1个点压到栈当中,开始构建虚树
3.枚举到下一个点u,计算u与栈顶点v的公共祖先lca
4.假设栈中栈顶下方的点为w(若栈中只有1个点就直跳过这一步),若w点的深度大于lca就把v向w连一条边,并且弹掉v,重复此步,否则就到下一步
5.若lca不是当前的v,那么就把lca和v连边,把v弹出,让lca成为栈顶元素(注:这个操作的意思是如果栈顶没有这个lca那么就压入),否则不做任何操作
6.最后把u压入栈中
7.回到3操作枚举下个点,直到枚举完了所有点
8.把栈顶v与栈顶下方的点为w连边,并且把v弹掉,这么做直到栈里只有一个点
9.栈里剩下的点就是虚树的根了

代码

#include<bits/stdc++.h>
#define pb push_back
#define maxn 400005
using namespace std;
struct edge{int to,nex;}e[maxn];
vector<int> g[maxn<<1];
int T,n,m,tot,num,top,Q,q,h,ans,cnt;
int head[maxn],dfn[maxn],low[maxn],f[maxn][25],deep[maxn],sta[maxn],val[maxn],a[maxn],b[maxn],fa[maxn];
void add(int s,int t){e[++cnt].to=t;e[cnt].nex=head[s];head[s]=cnt;}
void Tarjan(int x){
    dfn[x]=low[x]=++num;
    sta[++top]=x;
    for(int k=head[x];k;k=e[k].nex){
        if(!dfn[e[k].to]){
            Tarjan(e[k].to);
            low[x]=min(low[x],low[e[k].to]);
            if(low[e[k].to]>=dfn[x]){
                ++tot;
                while(sta[top]!=e[k].to)
                    g[tot].pb(sta[top]),g[sta[top]].pb(tot),top--;
                g[tot].pb(sta[top]),g[sta[top]].pb(tot),top--;
                g[tot].pb(x),g[x].pb(tot);}
        }
        else low[x]=min(low[x],dfn[e[k].to]);}
}
void dfs(int x){
    dfn[x]=++num;
    deep[x]=deep[f[x][0]]+1;
    val[x]=val[f[x][0]]+(x<=n);
    for(int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1];
    for(int i=0;i<g[x].size();i++){
        if(g[x][i]==f[x][0]) continue;
        f[g[x][i]][0]=x;
        dfs(g[x][i]);}
}
bool cmp(int a,int b){return dfn[a]<dfn[b];}
int lca(int x,int y){
    if(deep[x]<deep[y]) swap(x,y);
    for(int i=20;i>=0;i--) if(deep[f[x][i]]>=deep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];}
void solve(){
    top=ans=0;
    scanf("%d",&q);h=q;
    for(int i=1;i<=q;i++) scanf("%d",&a[i]),b[i]=a[i];
    sort(a+1,a+1+q,cmp);
    for(int i=1;i<=q;i++){
        if(!top) sta[++top]=a[i];
        else{
            int far=lca(a[i],sta[top]);
            while(dfn[sta[top]]>dfn[far]){
                if(dfn[sta[top-1]]<=dfn[far]){
                    fa[sta[top--]]=far;
                    if(sta[top]!=far) sta[++top]=far,b[++h]=far;
                    break;}
                else fa[sta[top]]=sta[top-1],top--;}
            sta[++top]=a[i];}
    }
    while(top>1) fa[sta[top]]=sta[top-1],top--;
    sort(b+1,b+1+h,cmp);
    for(int i=2;i<=h;i++) ans+=val[b[i]]-val[fa[b[i]]],fa[b[i]]=0;
    fa[b[1]]=0;
    printf("%d\n",ans+(b[1]<=n)-q);
}
int main(){
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1,s,t;i<=m;i++){
            scanf("%d%d",&s,&t);
            add(s,t);add(t,s);}
        tot=n;
        Tarjan(1);
        num=top=0;
        dfs(1);
        scanf("%d",&Q);
        while(Q--) solve();
        for(int i=0;i<=tot;i++) val[i]=head[i]=dfn[i]=low[i]=deep[i]=f[i][0]=0,g[i].clear();
        top=num=tot=cnt=0;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/chm_wt/article/details/81674864
今日推荐