【JZOJ5755】【SDOI2018】战略游戏

3 篇文章 0 订阅
1 篇文章 0 订阅

(File IO): input:game.in output:game.out
Time Limits: 10000 ms Memory Limits: 524288 KB

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 (1u<vn) ( 1 ≤ u < v ≤ n ) ,表示第 u个城市和第 v个城市之间有一条道路,同一对城市之间可能有多条道路连接,

第 m+1 是一个整数 q,表示游戏的局数,

接下来 q行,每行先给出一个整数 ∣S∣ (2≤∣S∣≤n),表示小 C 占领的城市数量,然后给出 ∣S∣ 个整数 s1,s2,…,s|S| (1s1<s2<<s|S|n) ( 1 ≤ s 1 < s 2 < ⋯ < s | S | ≤ n ) ,表示小 C 占领的城市。

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
Output
0
1
3
0
1
2
3

Data Constraint
对于 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。


Analysis
这题一看就知道和割点或点双有关
那么就要将点双缩起来,但是缩没那么好缩
这里用圆方树

什么是圆方树,对于一个点双,用一个方形代表,其他点用圆形表示,点双内的点与方形相连,那么构成一个圆方相间的树。
Analysis

这样只用对查询建一颗虚树,询问圆点个数

具体实现如下

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400100
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define fo2(i,x,g) for(int i=g.fir[x];i;i=g.nex[i])

using namespace std;

int n,m,f[20][N],dfn[N],low[N],cnt,q,top,sta[N],dep[N],s,d[N];
bool vis[N];
bool cmp(int a,int b){return dfn[a]<dfn[b];}

struct graph{
    int fir[N],nex[N+N],to[N+N],top;
    void clear(){
        memset(fir,0,sizeof fir);
        top=0;
    }
    void link(int x,int y){
        to[++top]=y;nex[top]=fir[x];fir[x]=top;
        to[++top]=x;nex[top]=fir[y];fir[y]=top;
    }
}g1,g2;

void tarjan(int u,int f){
    vis[u]=1;
    dfn[u]=low[u]=++top;
    sta[top]=u;
    fo2(i,u,g1)if(g1.to[i]!=f){
        int v=g1.to[i];
        if(!vis[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){
                g2.link(++cnt,u);
                for(;sta[top+1]!=v;--top)g2.link(cnt,sta[top]);
            }
        }else low[u]=min(low[u],dfn[v]);
    }
}
void dfs(int x,int fa){
    f[0][x]=fa;
    dfn[x]=++top;dep[x]=dep[fa]+1;
    fo2(i,x,g2)if(g2.to[i]!=fa)dfs(g2.to[i],x);
}
int lca(int x,int y){
    if(dep[x]>dep[y])return lca(y,x);
    fd(i,19,0)if(dep[f[i][y]]>=dep[x])y=f[i][y];
    if(x==y)return x;
    fd(i,19,0)if(f[i][y]!=f[i][x])x=f[i][x],y=f[i][y];
    return f[0][x];
}

int main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
    int t;scanf("%d",&t);
    while(t--){
        g1.clear();g2.clear();
        memset(vis,0,sizeof vis);
        scanf("%d %d",&n,&m);
        cnt=n;top=0;
        fo(i,1,m){
            int x,y;scanf("%d %d",&x,&y);
            g1.link(x,y);
        }
        tarjan(1,0);
        top=0;dfs(1,0);
        fo(j,1,19)fo(i,1,cnt)f[j][i]=f[j-1][f[j-1][i]];
        scanf("%d",&q);
        fo(i,1,q){
            int ans=0;
            scanf("%d",&s);
            fo(j,1,s)scanf("%d",&d[j]);
            sort(d+1,d+s+1,cmp);top=0;
            fo(j,1,s)if(!top)sta[top=1]=d[j];else{
                int u=lca(sta[top],d[j]);
                ans+=(dep[sta[top]]+1>>1)-(dep[u]+1>>1);
                while(top && dep[sta[top]]>=dep[u])--top;
                sta[++top]=u;sta[++top]=d[j];
            }ans+=(dep[sta[top]]+1>>1)-(dep[sta[1]]+1>>1);
            printf("%d\n",ans+(dep[sta[1]]&1)-s);
        }
    }
    fclose(stdin);fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值