bzoj 5329 [Sdoi2018]战略游戏

http://www.elijahqi.win/archives/3588
Description
省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏。
这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到
任意其他城市。现在小C已经占领了其中至少两个城市,小Q可以摧毁一个小C没占领的城市,同时摧毁所有连接这
个城市的道路。只要在摧毁这个城市之后能够找到某两个小C占领的城市u和v,使得从u出发沿着道路无论如何都不
能走到v,那么小Q就能赢下这一局游戏。
小Q和小C一共进行了q局游戏,每一局游戏会给出小C占领的城市集合S
你需要帮小Q数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。
Input
第一行包含一个正整数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占领的城市。
1<= T<= 10,
2<= n<= 10^5 且 n-1<= m<= 2*10^5,
1<= q<= 10^5,
对于每组测试数据,有Sigma|S|<= 2*10^5
Output
对于每一局游戏,输出一行,包含一个整数,表示这一局游戏中有多少个城市在小Q摧毁之后能够让他赢下这一局游戏。
Sample Input
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
Sample Output
0
1
3
0
1
2
3
想不到自己随便yy的竟然能1A
考虑先建圆方树 然后可以发现每次询问的其实是两个点 然后这两个点之间的圆点是我要求的
因为总询问点数有限 所以考虑建虚树 因此我们需要统计虚数上究竟有多少个圆点 还是挺好办的 看代码即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=2e5+10;
struct node{
    int y,next;
}data[N<<1],data1[N<<1];
int T,num,h[N],h1[N],q[N],top,cnt,dfn[N],low[N],Log[N],Q,a[N];
int dep[N],dis[N],fa[N][20],n,m,tot;
inline void insert1(int x,int y){
    data1[++cnt].y=y;data1[cnt].next=h1[x];h1[x]=cnt;
    data1[++cnt].y=x;data1[cnt].next=h1[y];h1[y]=cnt;
}
inline void tarjan(int x,int fa){
    dfn[x]=low[x]=++num;q[++top]=x;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa) continue;
        if (!dfn[y]){
            tarjan(y,x);low[x]=min(low[x],low[y]);
            if (low[y]>=dfn[x]){
                ++tot;insert1(x,tot);int v;
                do{
                    v=q[top--];insert1(v,tot);
                }while(v!=y);
            }
        }else low[x]=min(low[x],dfn[y]);
    }
}
inline void dfs(int x){
    dfn[x]=++num;dis[x]+=(x<=n);
    for (int i=h1[x];i;i=data1[i].next){
        int y=data1[i].y;if (y==fa[x][0]) continue;fa[y][0]=x;
        dep[y]=dep[x]+1;for (int j=1;j<=Log[dep[y]];++j) fa[y][j]=fa[fa[y][j-1]][j-1];
        dis[y]=dis[x];dfs(y);
    }
}
inline int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);int dis=dep[x]-dep[y];
    for (int i=0;i<=Log[dis];++i) if ((1<<i)&dis)x=fa[x][i];
    if (x==y) return x;
    for (int i=Log[dep[y]];~i;--i)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
inline bool cmp (const int &a,const int &b){return dfn[a]<dfn[b];}
int main(){
    freopen("bzoj5329.in","r",stdin);
    T=read();Log[0]=-1;for (int i=1;i<=2e5+10;++i) Log[i]=Log[i>>1]+1;
    while(T--){
        n=read();m=read();top=0;tot=n;cnt=0;memset(h1,0,sizeof(h1));
        memset(h,0,sizeof(h));num=0;memset(dfn,0,sizeof(dfn));
        memset(fa,0,sizeof(fa));
        for (int i=1;i<=m;++i){
            int x=read(),y=read();
            data[++num].y=y;data[num].next=h[x];h[x]=num;
            data[++num].y=x;data[num].next=h[y];h[y]=num;
        }num=0;Q=read();int p,ans;
        for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i,0);
        memset(dfn,0,sizeof(dfn));num=0;dfs(1);num=0;
        while(Q--){ans=0;p=read();
            for (int i=1;i<=p;++i) a[i]=read();
            sort(a+1,a+p+1,cmp);top=0;
            for (int i=1;i<=p;++i){
                if (!top) {q[++top]=a[i];continue;}
                int t=lca(a[i],q[top]);
                while(top>=1){
                    if (dep[q[top-1]]<=dep[t]){
                        if (t!=q[top]&&top) ans+=dis[q[top]]-dis[t];--top;
                        if (t!=q[top]) q[++top]=t;break;
                    }ans+=dis[q[top]]-dis[q[top-1]];--top;
                }q[++top]=a[i];
            }while(top>1) ans+=dis[q[top]]-dis[q[top-1]],--top;ans+=q[1]<=n;
            ans-=p;printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值