洛谷11月月赛 III Div.2 T3 基础博弈练习题

题目大意:

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轮游戏,给出每轮游戏使用的区间,请你判断每轮谁会赢

解题思路:

我们可以知道的是当 a i a_i ai为奇数时先手必胜
那么选 [ i − m , i − 1 ] [i-m, i-1] [im,i1]这段中的一个时,先手必败,因为这一段里面都可以转移到 i i i,转移到 i i i时先手和后手顺序就换过来
那么如果 a i a_i ai为偶数那么先手就必败,而选 [ i − m , i − 1 ] [i-m, i-1] [im,i1]这段中的一个时,先手必胜
我们维护一个 n e x t i next_i nexti,表示一个 j < = i j<=i j<=i a j a_j aj为奇数
那么如果 i i i必胜,那么 n e x t i − m − 1 next_{i-m-1} nextim1也必胜
对于每一个 i > m + 1 i>m+1 i>m+1,与 n e x t i − m − 1 next_{i-m-1} nextim1连边(其中 n e x t i − m − 1 next_{i-m-1} nextim1为父节点)
那么我们就会构建出一个树
若区间 [ l , r ] [l, r] [l,r]满足 l l l n e x t i next_i nexti的祖先那么先手必败, 否则先手必胜
d f s dfs dfs序判断

A c c e p t e d   c o d e : Accepted\ code: Accepted code:

#include
#include

using namespace std;

const int N = 1000005;
const long long mod = 1ll<<32;

struct Line {
     
	int to, next;
}e[N];

int n, m, q, l, r, A, B, C, P, cnt, type;
int a[N], dfn[N], last[N], next[N], size[N];
long long ans;

inline int rnd() {
     return A = (A * B + C) % P;}

inline void add(int x, int y) {
     
	e[++cnt] = (Line){
     y, last[x]}; last[x] = cnt;
}

void dfs(int x) {
     
	dfn[x] = ++cnt; size[x] = 1;
	for (int i = last[x]; i; i = e[i].next)
		dfs(e[i].to), size[x] += size[e[i].to];
	return;
}

int main() {
     
	scanf("%d %d %d %d", &n, &m, &q, &type);
	for (int i = 1; i <= n; ++i) {
     
		scanf("%d", &a[i]);
		if (a[i] & 1) next[i] = i;
		else next[i] = next[i-1];
		if (i > m + 1) add(next[i-m-1], i);
		else add(0, i);
	}
	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;
		else scanf("%d %d", &l, &r);
		if (l > r) swap(l, r);
		if (dfn[l] > dfn[next[r]] || dfn[l] + size[l] < dfn[next[r]] + size[next[r]])
			(ans += (long long)i * i) %= mod;
	}
	printf("%lld\n", ans);
}

你可能感兴趣的:(模拟赛,dfs序,博弈)