转化题意
题意要求求 ([l,r]) 在 (a[l]-1) 的情况下是否能先手必胜,转化一下,其实题意也可以理解为:先手第一步一定走 (l) ,是否是后手必胜。
逆推博弈
分类讨论,找出性质。
画图逆推以下,在确定右端点下,很明显有以下性质。
-
第一个到右端点r点的人,若右端点为奇数则胜,为偶数则败。
-
若第一个到 (i) 点的人必胜,则第一个到 ([i-m,i-1]) 的人必输。
-
若设第一个到 (i) 点的人必输的点叫必输点,则除了与右端点链接的必输点区间,其它必输点区间都至少含有连续 (m) 个必输点。
-
根据第 (2) 条可得,若 (i+1) 号点为必输点,则区间 ([i+1,min(i+m,r)]) 均为必输点,则等同于右端点在 (i) 点处,此时第一个到 (i) 点的人的胜负状态和第 (1) 条一样。
根据以上性质,总结出以下性质:
右端点为 (r) 的情况下,若 (r) 号点为必输点,则前面的胜负状态可以等价的把 (r-1) 点作为右端点,若 (r) 点为必胜点,则可以把 (r-m-1) 的点等价作为右端点。
那么,我们现在需要找对于 (r) 点来说, (l) 点是否是 (r) 点的必胜点,也可以看做 (r) 等价往左移后, (l) 是不是新 (r) 的必胜点,等价往左移 (r) 会有两种情况。一种是移动 (r) 直到 (r) 不大于 (l) ,可以刚好落到 (l) 点上,此时就可以根据 (l) 是否是奇数判断是否是必胜点,另一种是移动 (r) 直到 (r) 不大于 (l) ,然后发现 (l) 点无法被等价看作右端点( 无法落到 (l) )。对于第一种情况,(l) 是否是必胜点由 (a[l]) 是奇是偶决定,第二种情况,则 (l) 一定不能作为必胜点。
那么如何快速判断 (l) 是否是(r) 必胜点呢?我可以记录pr[i]表示i点作为右端点,除开i点外在 (i) 点左边的第一个必胜点(没有的话pr[i]=0),那么pr[i]的转移可以写出来:
(a[i]) 是奇数时,若 (a[i-m-1]) 是奇数,则 (pr[i]=i-m-1) ,否则 (pr[i]=pr[i-m-1]) 。
(a[i]) 是偶数时,若 (a[i-m-1]) 是奇数,则 (pr[i]=i-1) ,否则 (pr[i]=pr[i-1]) 。
这样处理 (pr) 后,把 (pr[i]) 当作 (i) 的父亲点,则 (i) 的祖先节点都是以 (i) 为右端点时的必胜点,简要证明:因为 (pr[i]) 在 (i) 是右端点时也可以等价看作右端点,则 (pr[pr[i]]) 也一定是必胜点,同理,祖先点都是。
那么此时题意就是: (l) 是否是 (r) 的祖先点。若是,先手胜,不是,后手胜。把后手胜的贡献加起来就是答案了。当然,若 (a[r]) 为偶数时,此时的必胜关系必须在 (l eq r) 的情况下才满足。构一下树,想一下为什么树的结构是这样的,思考一下为什么 (a[r]) 为偶数需要特判,如果明白了这个思路基本就没问题了。
树上关系
至于求是否是祖先点,静态树结构, (dfn) 序记录一下就行了。
#include <bits/stdc++.h>
using namespace std;
int n,m,Q,cnt,op;
int pr[1000005];
vector<int> q[1000005];
int dfn[1000005];
int siz[1000005];
int a[1000005];
int A,B,C,P;
inline int rnd(){return A=(A*B+C)%P;}
long long ans;
long long mod=(1ll<<32);
void dfs(int x){
cnt++;dfn[x]=cnt;
for(int i=0;i<q[x].size();i++){
int nx=q[x][i];
dfs(nx);
siz[x]+=siz[nx];
}
siz[x]++;
}
int main(){
scanf("%d%d%d%d",&n,&m,&Q,&op);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]%2==1){
int nx=max(i-m-1,0);
//cout<<nx<<endl;
if(a[nx]%2)pr[i]=nx;
else pr[i]=pr[nx];
}
else{
if(a[i-1]%2)pr[i]=i-1;
else pr[i]=pr[i-1];
}
//cout<<i<<" "<<pr[i]<<endl;
q[pr[i]].push_back(i);
}
dfs(0);
if(op)scanf("%d%d%d%d",&A,&B,&C,&P);
for(long long i=1;i<=Q;i++){
int l,r;
if(op){
l=rnd()%n+1,r=rnd()%n+1;
}
else{scanf("%d%d",&l,&r);}
if(l>r)swap(l,r);
if(l==r){
if(a[l]%2==0)ans=(ans+i*i)%mod;
continue;
}
if(m==0){
if(a[l]%2==0)ans=(ans+i*i)%mod;
continue;
}
if(dfn[l]+siz[l]-1>=dfn[r]&&dfn[l]<=dfn[r]){
}
else ans=(ans+i*i)%mod;
}
printf("%lld
",ans);
return 0;
}