核桃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;
}
posted @ 2025-04-30 19:19  zxh923  阅读(36)  评论(0)    收藏  举报