[agc005e]Sugigma: The Showdown

前言

要知道怎么判-1,接下来就好办许多了。

题目大意

一个有红边和蓝边的图,只保留红边或蓝边都是一颗树。
现在A和B初始都在一个点上,轮流进行,A先操作。
A每次可以不动或沿着相邻的红边走到另一节点。
B类似,但只能走蓝边。
A和B相遇游戏结束。
A要最大化游戏时间,B要最小化游戏时间,若A永远不会被B抓到输出-1。

做法

当A到达一条红树上的边(x,y)的端点时(这条边满足x和y在蓝树的距离大于2),B还没抓到A,显然就-1了。
发现除了这种情况A都会被抓到,因为只剩下距离为1和2的边。
以B的初始点为根,发现A每次就是往子树逃,或往兄弟逃,或往父亲逃。只要B步步下潜,就能抓到A。当A和B相距为1,A无法通过往上跑或往兄弟跑来逃脱(往父亲跑下一步也被抓了)。
删除红树上满足A一旦到达就会-1的那些边,然后做bfs求出A能能跑到的点(即到达该点前不会被B抓),A一定会选择在蓝树中深度最大的,然后待在那弃疗。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10;
int h[maxn],go[maxn*4],nxt[maxn*4],co[maxn*4],e[maxn][2];
int dfn[maxn],low[maxn],dep[maxn],d[maxn],dl[maxn],fa[maxn];
bool bz[maxn],pd[maxn];
int i,j,k,l,t,n,m,x,y,tot,top,ans,head,tail;
void add(int x,int y,int z){
    go[++tot]=y;
    co[tot]=z;
    nxt[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    fa[x]=y;
    dfn[x]=++top;
    int t=h[x];
    while (t){
        if (co[t]&&go[t]!=y){
            dep[go[t]]=dep[x]+1;
            dfs(go[t],x);
        }
        t=nxt[t];
    }
    low[x]=++top;
}
bool check(int x,int y){
    if (dfn[x]>dfn[y]) swap(x,y);
    if (dfn[x]<=dfn[y]&&low[x]>=low[y]) return dep[y]-dep[x]>2;
    if (fa[x]==fa[y]) return 0;else return 1;
}
void bfs(){
    int now,t;
    pd[x]=1;
    dl[tail=1]=x;
    while (head<tail){
        now=dl[++head];
        t=h[now];
        while (t){
            if (!co[t]&&!pd[go[t]]){
                d[go[t]]=d[now]+1;
                if (d[go[t]]<dep[go[t]]){
                    pd[go[t]]=1;
                    dl[++tail]=go[t];
                }
            }
            t=nxt[t];
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&x,&y);
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        //add(j,k,0);add(k,j,0);
        e[i][0]=j;e[i][1]=k;
    }
    fo(i,1,n-1){
        scanf("%d%d",&j,&k);
        add(j,k,1);add(k,j,1);
    }
    dfs(y,0);
    fo(i,1,n-1){
        j=e[i][0];k=e[i][1];
        if (check(j,k)) bz[j]=bz[k]=1;
        else{
            add(j,k,0);
            add(k,j,0);
        }
    }
    bfs();
    fo(i,1,n){
        if (pd[i]&&bz[i]){
            printf("-1\n");
            return 0;
        }
        else if (pd[i]) ans=max(ans,dep[i]*2);
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值