[JZOJ5085]游戏/[JZOJ3996]下棋

本文探讨了一种基于有向无环图的游戏策略问题。游戏中两人轮流操作移动石子,目标是确定先手是否有必胜策略。通过计算石子的Sg函数值,并利用线性基和bitset优化算法,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定一张n个点m条边的有向无环图,每条边有颜色ci。图上有q颗石子,每颗石子一开始在一个点上。
两个人轮流在图上进行操作。每次操作时,选择一个有出边(“下棋”一题没有这个限制)且点上有石子的点x,从点上取走一个石子,然后选择一个颜色集合S(可以为空),如果x的某条出边的颜色属于该集合,则在该边的终点上放上一颗石子。不能操作者输。
请问先手是否存在必胜策略。

n200,m5000,q105,ci5000


题目分析

可以发现每个石子其实看成一个单独的游戏。
考虑如何计算一个石子的Sg函数值。

Sg(x)=mexsS{xor(x,y)sSg(y)}

这个怎么算呢?我们把同颜色的边先异或起来,然后对这些数求线性基,假设i是最小的线性相关的位(线性基该位为0),那么答案显然就是2i
然后就可以用bitset瞎压一下位来做了。
注意,如果必须选择有出边的点的话,一个没有出边的点Sg值应该是0,如果没有这个限制就是1

代码实现

#include <iostream>
#include <cstdio>
#include <bitset>
#include <cctype>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int N=205;
const int M=5005;
const int C=5005;

typedef bitset<N> B;

B base[N],sg[N],cbit[C],ans;
int nxt[M],tov[M],col[M];
bool vis[N];
int last[N];
int used[C];
int ext[C];
int n,m,tot,q;

void insert(int x,int y,int z){tov[++tot]=y,nxt[tot]=last[x],col[tot]=z,last[x]=tot;}

void dfs(int x)
{
    if (vis[x]) return;
    vis[x]=1;
    if (!last[x]) return;
    for (int i=last[x];i;i=nxt[i]) dfs(tov[i]);
    ext[0]=0;
    for (int i=last[x];i;i=nxt[i])
    {
        if (used[col[i]]!=x) cbit[col[i]].reset(),used[ext[++ext[0]]=col[i]]=x;
        cbit[col[i]]^=sg[tov[i]];
    }
    for (int i=0;i<=n;i++) base[i].reset();
    for (int i=1;i<=ext[0];++i)
    {
        for (int j=n;j>=0;--j)
            if (cbit[ext[i]].test(j))
                if (base[j].test(j)) cbit[ext[i]]^=base[j];
                else
                {
                    base[j]=cbit[ext[i]];
                    break;
                }
    }
    for (int i=0;i<=n;++i)
        if (base[i].none())
        {
            sg[x].set(i);
            break;
        }
}

int main()
{
    freopen("game.in","r",stdin),freopen("game.out","w",stdout);
    n=read(),m=read();
    for (int i=1,x,y,z;i<=m;++i) x=read(),y=read(),z=read(),insert(x,y,z);
    for (int i=1;i<=n;++i) dfs(i);
    for (q=read();q--;) ans^=sg[read()];
    printf("%d\n",ans.any());
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值