核桃NOI周赛56 长条格子
好题,可惜赛时没有想出来。
首先有一个搞笑做法,设 \(f_{i,j}\) 表示当前到第 \(i\) 格且 \(a_i=j\) 是必胜还是必败,这个东西可以得到 \(10\) 分。
然后我们简单观察一下,发现如果在一个位置,你不想走,那对手也肯定不想走,所以你们两个会一直原地减 \(1\),直到必须走为止。于是我们发现,\(a_i\) 具体是几不重要,重要的是它的奇偶性。
于是我们把第一个做法第二维改成 \(0/1\) 就获得了 \(20\) 分(实测需要循环写才能 \(20\) 分,记搜疑似被卡常)。
现在我们继续观察,注意到一个位置的 \(a_i\) 如果是奇数,那么如果后面有一个位置必败,我可以直接走过去,否则我就停一下让对手走过去。有次我们得出当 \(a_i\) 为奇数时此位置必胜。
但是这个东西仍然不够,注意到如果一个位置必败,那么所有能跳到这个位置的位置都必胜。由此我们得出,对于一个必败位置 \(p\),上一个必败位置 \(q\) 一定是 \([1,p-k-1]\) 中的最后一个 \(a_q\) 为偶数的位置。
于是我们设 \(ne_i\) 表示 \(i\) 前面第一个必败位置,然后对于子任务 \(3\),我们用倍增快速向前跳即可。
现在只剩最后一步,支持修改了(这里我赛时一直在想基于倍增的东西,所以没有想出来)。
我们考虑分块。
接下来我们认为 \(a_i\) 为偶数的位置为 \(0\),否则为 \(1\)。
对于每个块内,我们维护每个位置左边的第一个 \(0/1\)(没有就设为 \(0\)),以及如果从一个位置出发跳 \(0/1\) 跳到的最左边一个位置在哪。
这些东西都可以通过扫一遍预处理,接下来我们处理修改和询问。
对于修改,如果 \(d\) 是偶数,可以直接忽略。
否则,对于散块,我们直接暴力重构,整块打一个取反标记即可(注意重构之前先把打的标记作用上去并清空标记)。
然后我们考虑询问,对于散块,直接暴力扫一遍往前跳。
然后就到了一个难点,块间应该怎么跳。首先注意到如果上一块末尾是 \(r\),当前位置是 \(pos\),那么 \(pos\ge r+k+1\) 的所有情况本质相同,直接认为是在 \(r+k+1\) 即可。
然后如果跳 \(k\) 格不在这个块内,直接忽略这个块即可。
否则,我们直接跳 \(k\) 格,然后找到这个位置左边第一个 \(0/1\)(取决于是否有标记),然后从这个位置跳到这个位置能跳到的块内最左边。最后重复跳块过程即可。
于是我们就做完了,感觉并没有黑题的难度,整体想下来还是挺自然的。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 200005
#define K 505
#define pii pair<int,int>
#define x first
#define y second
#define mod 1000000000
#define inf 2e18
using namespace std;
int T=1,n,m,q,len,cnt,id[N],L[N],R[N],a[N];
int ne0[N],las0[N],ne1[N],las1[N],lzy[K];
void build(int k){
int p0=0,p1=0;
for(int i=L[k];i<=R[k];i++){
ne0[i]=ne1[i]=0;
}
for(int i=L[k];i<=R[k];i++){
if(!a[i])p0=i;
else p1=i;
if(p0)ne0[i]=p0;
if(p1)ne1[i]=p1;
}
for(int i=L[k];i<=R[k];i++){
las0[i]=las1[i]=i;
}
for(int i=L[k]+m+1;i<=R[k];i++){
if(ne0[i-m-1])las0[i]=las0[ne0[i-m-1]];
if(ne1[i-m-1])las1[i]=las1[ne1[i-m-1]];
}
}
void solve(int cs){
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]&=1;
}
len=sqrt(n);
for(int i=1;i<=n;i++){
id[i]=(i-1)/len+1;
if(!L[id[i]])L[id[i]]=i;
R[id[i]]=i;
}
cnt=id[n];
for(int k=1;k<=cnt;k++){
build(k);
}
while(q--){
int op,l,r,d;
cin>>op>>l>>r;
if(op==1){
cin>>d;
if(d&1){
if(id[l]==id[r]){
for(int i=l;i<=r;i++){
a[i]^=1;
}
if(lzy[id[l]]){
lzy[id[l]]^=1;
for(int i=L[id[l]];i<=R[id[l]];i++){
a[i]^=1;
}
}
build(id[l]);
}
else{
for(int i=l;i<=R[id[l]];i++){
a[i]^=1;
}
if(lzy[id[l]]){
lzy[id[l]]^=1;
for(int i=L[id[l]];i<=R[id[l]];i++){
a[i]^=1;
}
}
build(id[l]);
for(int i=L[id[r]];i<=r;i++){
a[i]^=1;
}
if(lzy[id[r]]){
lzy[id[r]]^=1;
for(int i=L[id[r]];i<=R[id[r]];i++){
a[i]^=1;
}
}
build(id[r]);
for(int i=id[l]+1;i<id[r];i++){
lzy[i]^=1;
}
}
}
}
else{
if(a[l]^lzy[id[l]]){
cout<<"A\n";
continue;
}
if(id[l]==id[r]){
int pos=r+m+1;
for(int i=r;i>=l;i--){
if(!lzy[id[l]]){
if(pos-i>=m+1&&!a[i]){
pos=i;
}
}
else{
if(pos-i>=m+1&&a[i]){
pos=i;
}
}
}
if(pos==l)cout<<"B\n";
else cout<<"A\n";
}
else{
int pos=r+m+1;
for(int i=r;i>=L[id[r]];i--){
if(!lzy[id[r]]){
if(pos-i>=m+1&&!a[i]){
pos=i;
}
}
else{
if(pos-i>=m+1&&a[i]){
pos=i;
}
}
}
for(int i=id[r]-1;i>id[l];i--){
if(pos-m-1<L[i])continue;
pos=min(R[i]+m+1,pos);
if(!lzy[i]){
if(!ne0[pos-m-1])continue;
pos=ne0[pos-m-1];
pos=las0[pos];
}
else{
if(!ne1[pos-m-1])continue;
pos=ne1[pos-m-1];
pos=las1[pos];
}
}
if(pos-m-1>=l){
pos=min(pos,R[id[l]]+m+1);
if(!lzy[id[l]]&&ne0[pos-m-1])pos=ne0[pos-m-1];
else if(lzy[id[l]]&&ne1[pos-m-1])pos=ne1[pos-m-1];
for(int i=pos;i>=l;i--){
if(!lzy[id[l]]){
if(pos-i>=m+1&&!a[i]){
pos=i;
}
}
else{
if(pos-i>=m+1&&a[i]){
pos=i;
}
}
}
}
if(pos==l)cout<<"B\n";
else cout<<"A\n";
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}

浙公网安备 33010602011771号