【洛谷P5652】基础博弈练习题【dfs】【博弈论】
题目:
题目链接:https://www.luogu.org/problem/P5652
YSGH和YGSH在打膈膜,YSGS在旁边围观。
规则是这样的,先给定一个正整数和一个个数序列,一开始有一个棋子在的第一个位置,并将减去。此后双方轮流操作,每次操作,假设当前棋子在,可以把棋子移到一个位置,满足且,然后将减,YSGH先手,谁先不能操作谁输。
众所周知,YSGH和YGSH都是绝顶聪明的,所以两人都会使用最优策略。
而隔膜使用的序列是一个序列的一个连续非空子序列,当然序列和每次隔膜使用的序列都是YSGS定的。
现在他们进行了轮游戏,给出每轮游戏使用的区间,请你判断每轮谁会赢。
思路:
如果位置先手是必败的,那么先手都只要跳到,就使得当前选择的后手必败。也就是先手必胜。
那么如果均为先手必胜,那么当选择到时,如果为奇数,那么最终就是先手取完这堆石子,那么必败的石子堆时后手先取,所以先手必胜。反之,如果为偶数,先手必败。
所以我们维护出每一个位置的,表示满足为奇数且且尽量大的。
所以如果必胜,那么也必胜。所以如果我们从向连边,那么区间如果满足是的祖先,那么就先手必败,否则先手必胜。
判断是否为祖先节点可以用序来解决。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1000010;
const ll MOD=(1LL<<32);
int n,m,Q,l,r,type,tot,a[N],head[N],nxt[N],dfn[N],size[N];
ll ans;
struct edge
{
int next,to;
}e[N];
int A,B,C,P;
inline int rnd(){return A=(A*B+C)%P;}
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs(int x)
{
dfn[x]=++tot; size[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
dfs(v);
size[x]+=size[v];
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&m,&Q,&type);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]&1) nxt[i]=i;
else nxt[i]=nxt[i-1];
if (i-m-1>0) add(nxt[i-m-1],i);
else add(0,i);
}
tot=0;
dfs(0);
if (type) scanf("%d%d%d%d",&A,&B,&C,&P);
for (int i=1;i<=Q;i++)
{
if (type)
{
l=rnd()%n+1,r=rnd()%n+1;
if (l>r) swap(l,r);
}
else scanf("%d%d",&l,&r);
if (!(dfn[l]<=dfn[nxt[r]] && dfn[l]+size[l]>=dfn[nxt[r]]+size[nxt[r]]))
ans=(ans+1LL*i*i)%MOD;
}
printf("%lld",ans);
return 0;
}