CF八九月杂题选做

CF八九月杂题选做

1863F

有一个长为 n 的数组 a1,a2,,an

在每次操作中,你可以把数组分成两段,留下一段,扔掉另一段,要求前者的异或和不小于后者的。重复操作,直到只剩下一个数为止。

对于每个 i,问最后剩下来的可不可能是第 i 个数。

t 组数据。1t10 0001n10 0001ai260n10 000

容易想到一个暴力DP,也即设 fl,r 为是否可能保留下 [l,r],最初 f[1,n]=1

对于 f[l,r],可以从 f[x,r](x<l),f[l,y](y>r) 两方面转移,同样是比较异或和进行转移。

时间复杂度 O(n3)

如何优化? 发现 n10000,复杂度必然是卡死 O(n2)

我们来发现一下性质。设 s[l,r][l,r] 的异或和,则若 s[l,k]s[k+1,r],就可以实施转移。但条件?

发现硬生生比较是不可取的,而注意到 s[l,k]s[k+1,r]=s[l,r],这里就引导我们发现性质了。

引理:对于三个数 i,j,k,若 k=ij,设 x2log2k。则若 i&x=x,则有 i>j。当 x=0 时有 i=j

证明:当 x=0 时,显然 i=j,下面我们讨论 x0 的情况。

根据异或运算的性质,显然有 k 的第 i 位是 1 则说明 i,j 其中之一为 1,而若为 0 则说明二者这一位相同,比较无意义。

那么最高位的 1 就是第一个二者不同的二进制位,而 x 的意义也就是如此,谁占有这个 1 位谁更大,故引理成立。

这样我们就发现,我们仅仅只需要比较这一位即可得到二者大小。

有什么用处呢?这是很妙的。我们成功将比较 s[l,x],s[x+1,r] 转化为了比较 s[l,x],s[l,r]

也即若 f[l,r] 是否可以被更新,当它作为前部分时,只要有一个合法的区间 f[l,x](x>r),满足 s[l,x] 的最高位在 s[l,r] 中同样为1,即可实现转移。

那么这是是容易维护的,发现这个最高位不过 64 种可能性,完全可以用一个数进行压缩,也即我们维护 gl 表示目前已经知晓的区间中,左端点为 l 且可以被转移到的区间的最高位的 1 的按位或和即可。

只要二者的按位与不为0,则必定可以转移。(当然需要特判一下是否出现了 s[l,r]=0 的扯淡情况。可以再开一个数组维护。

左边也是同理的。

#define N 10550
#define int long long
bool f[N][N];
int s[N],h[N],g[N],mnl[N],mnr[N];
int get(int x){
	int res=__builtin_clzll(x);
	return 1ll<<(63-res);
}
signed main(){
	ios::sync_with_stdio(false);
	int T;cin>>T;
	while(T--){
		int n;cin>>n;
		for(int i=1;i<=n;i++)cin>>s[i],s[i]^=s[i-1],h[i]=g[i]=mnl[i]=mnr[i]=0;
		f[1][n]=1;h[1]|=get(s[n]),g[n]|=get(s[n]);
		if(!s[n])mnl[1]=mnr[n]=1;
		for(int len=n-1;len;--len){
			for(int l=1,r=len;r<=n;l++,r++){
				f[l][r]=0;int k=s[r]^s[l-1];
				if(mnl[l]||(k&h[l]))f[l][r]=1;
				if(mnr[r]||(k&g[r]))f[l][r]=1;
				if(f[l][r])h[l]|=get(k),g[r]|=get(k),mnl[l]|=(k==0),mnr[r]|=(k==0);
			}
		}
		for(int i=1;i<=n;i++)cout<<f[i][i];
		cout<<"\n";
	}
}

1859D

1859E

1859F

1858D

1858E

1858F

1860F

1864D

1864E

1864F

1861E

posted @   spdarkle  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示