【洛谷P5652】基础博弈练习题【dfs】【博弈论】

题目:

题目链接:https://www.luogu.org/problem/P5652
YSGH和YGSH在打膈膜,YSGS在旁边围观。

规则是这样的,先给定一个正整数 m m m和一个 n n n个数序列 B B B,一开始有一个棋子在 B B B的第一个位置,并将 B 1 B_1 B1减去 1 1 1。此后双方轮流操作,每次操作,假设当前棋子在 i i i,可以把棋子移到一个位置 j j j,满足 j ∈ [ i , m i n ( i + m , n ) ] j\in[i,min(i+m,n)] j[i,min(i+m,n)] B j > 0 B_j>0 Bj>0,然后将 B j B_j Bj 1 1 1,YSGH先手,谁先不能操作谁输。

众所周知,YSGH和YGSH都是绝顶聪明的,所以两人都会使用最优策略。

而隔膜使用的序列 B B B是一个序列 A A A的一个连续非空子序列,当然序列 A A A和每次隔膜使用的序列 B B B都是YSGS定的。

现在他们进行了 q q q轮游戏,给出每轮游戏使用的区间,请你判断每轮谁会赢。


思路:

如果位置 i i i先手是必败的,那么 [ i − m , i − 1 ] [i-m,i-1] [im,i1]先手都只要跳到 i i i,就使得当前选择的后手必败。也就是先手必胜。
那么如果 [ i + 1 , i + m ] [i+1,i+m] [i+1,i+m]均为先手必胜,那么当选择到 i i i时,如果 i i i为奇数,那么最终就是先手取完这堆石子,那么必败的石子堆时后手先取,所以先手必胜。反之,如果为偶数,先手必败。
所以我们维护出每一个位置 i i i n x t [ i ] nxt[i] nxt[i],表示满足 a j a_j aj为奇数且 j < i j<i j<i j j j尽量大的 j j j
所以如果 i i i必胜,那么 n x t [ i − m − 1 ] nxt[i-m-1] nxt[im1]也必胜。所以如果我们从 n x t [ i − m − 1 ] nxt[i-m-1] nxt[im1] i i i连边,那么区间 [ l , r ] [l,r] [l,r]如果满足 l l l n x t [ r ] nxt[r] nxt[r]的祖先,那么就先手必败,否则先手必胜。
判断是否为祖先节点可以用 d f s dfs dfs序来 O ( 1 ) O(1) O(1)解决。


代码:

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值