AtCoder Grand Contest 005 E - Sugigma: The Showdown 博弈论+贪心

题意

给出一个无向图,边分为红边和蓝边,其中红边和蓝边分别构成一棵树。现在两个人各在树上一个点。每人轮流操作,每次可以不走或走到一个与当前点有边相连的点。其中先手只能走红边,后手只能走蓝边。若某刻两个人位于同一个点,游戏结束。先手要最大化操作次数,后手要最小化操作次数。问最终的操作次数是多少。
n<=200000

分析

首先要知道怎么判-1。
先把蓝边树建出来,对于某条红边(x,y),若在蓝树上x到y路径长度大于2,且在先手走到x或y上时后手还未抓到先手,则为-1。
那么我们把长度大于2的边删掉,剩下的就只能是1或2。
不难发现只走长度为1或2的边是不可能走出蓝树的子树的。
那么先手的最优策略就是走到一个能走到的深度最大的点,然后弃疗。
这样就可以做了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=200005;

int n,cnt,last[N],fa[N],dep[N],dis[N],q[N],dfn[N],mx[N],tim,a[N][2],sx,sy;
bool vis[N],win[N];
struct edge{int to,next,col;}e[N*4];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void addedge(int u,int v,int col)
{
    e[++cnt].to=v;e[cnt].col=col;e[cnt].next=last[u];last[u]=cnt;
    e[++cnt].to=u;e[cnt].col=col;e[cnt].next=last[v];last[v]=cnt;
}

void dfs(int x)
{
    dep[x]=dep[fa[x]]+1;dfn[x]=++tim;
    for (int i=last[x];i;i=e[i].next)
        if (e[i].to!=fa[x]) fa[e[i].to]=x,dfs(e[i].to);
    mx[x]=++tim;
}

bool check(int x,int y)
{
    if (dfn[x]>dfn[y]) swap(x,y);
    if (dfn[x]<dfn[y]&&mx[x]>dfn[y]) return dep[y]-dep[x]>2;
    if (fa[x]==fa[y]) return 0;
    return 1;
}

void bfs()
{
    int h=1,t=1;q[1]=sx;vis[sx]=1;
    while (h<=t)
    {
        int x=q[h++];
        for (int i=last[x];i;i=e[i].next)
            if (!e[i].col&&!vis[e[i].to])
            {
                dis[e[i].to]=dis[x]+1;
                if (dis[e[i].to]<dep[e[i].to]) vis[e[i].to]=1,q[++t]=e[i].to;
            }
    }
}

int main()
{
    n=read();sx=read();sy=read();
    for (int i=1;i<n;i++) a[i][0]=read(),a[i][1]=read();
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        addedge(x,y,1);
    }
    dep[0]=-1;dfs(sy);
    for (int i=1;i<n;i++)
    {
        if (check(a[i][0],a[i][1])) win[a[i][0]]=win[a[i][1]]=1;
        else addedge(a[i][0],a[i][1],0);
    }
    bfs();
    for (int i=1;i<=n;i++) if (win[i]&&vis[i]) {puts("-1");return 0;}
    int ans=0;
    for (int i=1;i<=n;i++) if (vis[i]) ans=max(ans,dep[i]*2);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Practice Contest #B - インタラクティブ練習 (Interactive Sorting) 是一道比较有趣的题目。它是一道交互式的排序题目,需要你与一个神秘程序进行交互,以便将一串无序的数字序列排序。 具体来说,这个神秘程序会给你一个长度为 $N$ 的数字序列,然后你需要通过询问它两个数字的大小关系,来逐步确定这个序列的排序顺序。每次询问之后,神秘程序都会告诉你两个数字的大小关系,比如第一个数字比第二个数字小,或者第二个数字比第一个数字小。你需要根据这个信息,来调整这个数字序列的顺序,然后再向神秘程序询问下一对数字的大小关系,以此类推,直到这个数字序列被完全排序为止。 在这个过程中,你需要注意以下几点: 1. 你最多只能向神秘程序询问 $Q$ 次。如果超过了这个次数,那么你的程序会被判定为错误。 2. 在每次询问之后,你需要及时更新数字序列的顺序。具体来说,如果神秘程序告诉你第 $i$ 个数字比第 $j$ 个数字小,那么你需要将这两个数字交换位置,以确保数字序列的顺序是正确的。如果你没有及时更新数字序列的顺序,那么你的程序也会被判定为错误。 3. 在询问的过程中,你需要注意避免重复询问。具体来说,如果你已经询问过第 $i$ 个数字和第 $j$ 个数字的大小关系了,那么你就不需要再次询问第 $j$ 个数字和第 $i$ 个数字的大小关系,因为它们的大小关系已经被确定了。 4. 在排序完成之后,你需要将排序结果按照从小到大的顺序输出。如果你输出的结果不正确,那么你的程序也会被判定为错误。 总的来说,这道题目需要你熟练掌握交互式程序设计的技巧,以及排序算法的实现方法。如果你能够熟练掌握这些技巧,那么就可以顺利地完成这道非传统题了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值