题目链接: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] [i−m,i−1]先手都只要跳到 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 jj<i且 j j j尽量大的 j j j。
所以如果 i i i必胜,那么 n x t [ i − m − 1 ] nxt[i-m-1] nxt[i−m−1]也必胜。所以如果我们从 n x t [ i − m − 1 ] nxt[i-m-1] nxt[i−m−1]向 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
#include
#include
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;
}