22.8.23 总结

CF1720D1

由于该题涉及到了子序列,我们先一眼看出这一题是一道动态规划。
根据以往的经验,于是我们先列出状态 \(f_i\) 表示以 \(i\) 结尾的 \(b\) 序列最长长度。
\(O(n^2)\) 暴力方程为: \(f_i=\max_{0\le j <i}(f_j+1),(a_j⊕i<a_i⊕j)\).

瓶颈为寻找最大的 \(f_j\) 需在 \(0\le j <i\) 中寻找。
在这一题中 \(a_i\le 200\) ,那么我们思考:
\(i>j+256\) 时,不考虑二进制后面 \(8\) 位,\(i>j\).

由于 \(a_i\le200\),所以 \(a_i\) 只能影响后 \(8\) 位。
所以 \(a_j⊕i<a_i⊕j\) 一定对 \(i>j+256\) 不成立。

所以新的方程是: \(f_i=\max_{i-256+1\le j <i}(f_j+1),(a_j⊕i<a_i⊕j)\).
复杂度 \(O(256n)\).

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=3e5+10,M=256;
int t,n,a[N],f[N],ans;
int main() {
	scanf("%d",&t);
	for(; t; t--) {
		scanf("%d",&n);
		ans=0;
		for(int i=0; i<n; i++) scanf("%d",&a[i]);
		for(int i=0; i<n; i++) f[i]=1;
		for(int i=0; i<n; i++)
			for(int j=max(0,i-M+1); j<i; j++)
				if((a[j]^i)<(a[i]^j))
					f[i]=max(f[i],f[j]+1);
		for(int i=0; i<n; i++) ans=max(ans,f[i]);
		printf("%d\n",ans);
	}
	return 0;
}

CF1716D

显然动态规划。设 \(f_{i,j}\) 为走了 \(i\) 步,走到 \(j\) 的方案数,令 \(d=i+k-1\)
原始方程为 \(f_{i,j}=\sum_{k>0} f_{i-1,j-kd}\)
用前缀和优化一下,变成了 \(f_{i,j}=sum_j\).
\(sum_j=sum_{j-d}+f_{i-1,j}\).
由于最多走 \(\sqrt{n}\) 步,时间复杂度 \(O(n\sqrt n)\),空间复杂度用滚动数组降到 \(O(n)\).

#include<cstdio>
using namespace std;
const int N=5e5+10,mod=998244353;
int n,k,m,t,f[2][N],sum[N],ans[N];
int main() {
	scanf("%d%d",&n,&k);
	while(t<=n) {m++; t+=(m+k-1);}
	f[0][0]=1;
	for(int i=1; i<=m; i++) {
		int d=i+k-1;
		for(int j=0; j<=n; j++) sum[j]=((j-d>=0?sum[j-d]:0)+f[i&1^1][j])%mod;
		for(int j=0; j<=n; j++) f[i&1][j]=(j-d>=0?sum[j-d]:0)%mod;
		for(int j=0; j<=n; j++) ans[j]=(ans[j]+f[i&1][j])%mod;
	}
	for(int i=1; i<=n; i++) printf("%d ",ans[i]%mod);
	return 0;
}

CF1715D

一道构造。
对于这种二进制操作的题目,我们应该拆位处理。
我们先把所有限制条件建成图。
对于一条边,如果某一位为0,那么其两个端点都在必须这位为0。
填完所有的0之后,考虑字典序最小,尝试把一些1变成0。

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=2e5+10,M=30,inf=(1<<30)-1;
int n,q,tot,head[N],ver[2*N],nxt[2*N],edge[2*N];
int p[N],vis[N];
void addedge(int x,int y,int z) {
	ver[++tot]=y; 
	edge[tot]=z;
	nxt[tot]=head[x];
	head[x]=tot;
}
int main() {
	scanf("%d%d",&n,&q);
	for(int i=1; i<=n; i++) p[i]=inf;
	for(int i=1,x,y,z; i<=q; i++) {
		scanf("%d%d%d",&x,&y,&z);
		addedge(x,y,z); addedge(y,x,z);
		vis[x]=vis[y]=1;
		for(int j=M-1; j>=0; j--) {
			if(!(z&(1<<j))) {
				p[x]&=(inf^(1<<j));
				p[y]&=(inf^(1<<j));
			}
		}
	}
	for(int x=1; x<=n; x++) {
		for(int j=M-1; j>=0; j--) {
			bool flag=1;
			if(p[x]==(p[x]&(inf^(1<<j)))) continue;
			p[x]&=inf^(1<<j);
			for(int i=head[x]; i; i=nxt[i]) {
				int y=ver[i],z=edge[i];
				if((p[x]|p[y])!=z) flag=0;
			}
			if(!flag) p[x]|=(1<<j);
		}
	}
	for(int i=1; i<=n; i++) {
		if(!vis[i]) {printf("0 "); continue;}
		printf("%d ",p[i]);
	}
	return 0;
}

CF1718A2

DP好题。
我们设 \(f_i\) 为前 \(i\) 个数都变成 \(0\) 需要的秒数,那么如何转移呢?
首先我们知道 \(f_i\) 可以由 \(f_{i-1}+1\) 转移而来.
那么对 \(f_{i-2}\) 呢? 若 \(a_i⊕a_{i-1}=0(a_i=a_{i-1})\) 那么 \(f_i=f_{i-2}+1\).
那么对 \(f_{i-3}\) 呢? 若 \(a_i⊕a_{i-1}⊕a_{i-2}=0\) 那么 \(f_i=f_{i-3}+2\).
这是因为: \(a_{i-2}⊕a_{i-2}=0,a_{i-1}⊕a_{i-2}=a_i,a_i⊕a_i=0\).
那么对 \(f_j\) 呢? 若 \(a_j⊕a_{j+1}⊕...⊕a_i=0\) 那么 \(f_i=f_j+i-j\).
用前缀异或和。

#include<algorithm>
#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
const int N=100010;
int n,a[N],b[N],t,f[N];
map<int,int> mp;
int main() {
	scanf("%d",&t);
	for(; t; t--) {
		scanf("%d",&n); mp.clear(); mp[0]=0;
		for(int i=1; i<=n; i++) scanf("%d",&a[i]);
		for(int i=1; i<=n; i++) b[i]=b[i-1]^a[i];
		for(int i=1; i<=n; i++) {
			f[i]=f[i-1]+(a[i]>0);
			if(mp.count(b[i])) {
				int j=mp[b[i]];
				f[i]=min(f[i],f[j]+i-(j+1));
			}
			mp[b[i]]=i;
		}
		printf("%d\n",f[n]);
	} 
	return 0;
}
posted @ 2022-08-23 20:06  s1monG  阅读(26)  评论(0编辑  收藏  举报