《CF1863》 解题报告

发布时间 2023-09-01 22:16:16作者: daduoli

题面传送器

首先有一个 \(naive\) 的做法。

直接 \(O(n^3)\) 暴力判断。

考虑寻找突破口。

假如给了你一个序列,异或值为 \(S\) ,那么实际上假如中间有一个断点 \(mid\) ,那么我们最终决定保留哪一段,实际上是看 \(S\) 的最高位 \(1\) 的位置来比较的,所以我们只需要管最高位的 \(1\) 就可以了。(因为如果为 \(0\) 就是要么都是 \(1\) ,要么都是 \(0\)

所以我们只需要关注最高位即可。

我们记一个 \(L_i,R_i\) 分别表示以 \(i\) 开始时,这一段序列要想成功,可以有的异或值有哪些(只要有其中一位,就能成功),而 \(R\) 就是以他结束,同理。

然后如果一个区间可行,记他的最高位为 \(k\) ,那么他左端点的 \(L\) 或上 \(1<<k\) ,右端点 \(R\) 或上 \(1<<k\)

不过如果异或值为 \(0\) 就要特判,因为这时候 \(l,r\) 不管是什么都可以。

因为你从中间截断,一定会有 \(x^y=S=0\) 所以 \(x=y\) ,所以从里面随便选择一个就可以了。

总时间复杂度 \(O(n^2)\)

启示:

如果有 \(x^y=S\) ,然后让你比较 \(x,y\) 的大小,直接看 \(S\) 最高位的 \(1\) 即可。

点击查看代码
#include<bits/stdc++.h>
typedef long long LL;

using namespace std;
const int MAXN=10010;
LL T;
LL n,b[MAXN],s[MAXN],L[MAXN],R[MAXN];
bool dp[MAXN][MAXN];
int main () {
	scanf("%lld",&T);
	while(T--) {
		scanf("%lld",&n);
		for(int i=1;i<=n;++i) {
			scanf("%lld",&b[i]);
			s[i]=s[i-1]^b[i];
			L[i]=R[i]=0;
		}
		for(int i=1;i<=n;++i) 
			for(int j=1;j<=n;++j) dp[i][j]=0;
		dp[1][n]=1;
		for(int i=n;i>=1;--i) {
			int RR=n-i+1;
			for(int j=1;j<=RR;++j) {
				int r=j+i-1;
				LL ls=s[r]^s[j-1];
				if(L[j]==-1||(ls&L[j])) dp[j][r]=1;
				if(R[r]==-1||(ls&R[r])) dp[j][r]=1;
				if(!dp[j][r]) continue;
				if(!ls) {
					L[j]=R[r]=-1;
					continue;
				}
				LL uu=63-__builtin_clzll(ls);
				L[j]|=(1ll<<uu);
				R[r]|=(1ll<<uu);
			}
		}
		for(int i=1;i<=n;++i) printf("%d",dp[i][i]);
		puts("");
	}
	return 0;
}