2022.10.26 总结

CF1742G

考虑拆位,先把高位的填成 1 ,后面再考虑填上低位的。
把每一位能填的数存进数组里。
从高位往低位填,每一位填时,尽量把低位也顺便填上。

code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,logm=32;
int n,a[N],t,res[N],w[logm],vis[N];
struct node {
	int id,v;
};
bool cmp(node x,node y) {
	return x.v>y.v;
}
vector<node> v[logm];
int main() {
	scanf("%d",&t);
	for(; t; t--) {
		for(int i=logm-1; i>=0; i--) v[i].clear();
		scanf("%d",&n);
		for(int i=1; i<=n; i++) {
			scanf("%d",&a[i]);
			for(int j=0; j<logm; j++) {
				if(a[i]&(1<<j)) {
					node f;
					f.id=i; f.v=a[i];
					v[j].push_back(f);
				}
			}
		}
		memset(w,0,sizeof(w));
		int tot=0;
		for(int i=logm-1; i>=0; i--) {
			if(w[i]) continue;
			int q=0;
			for(int k=0; k<logm; k++) if(!w[k]) q|=1<<k;
			for(int j=0; j<v[i].size(); j++)
				v[i][j].v&=q;
			sort(v[i].begin(),v[i].end(),cmp);
			if(v[i].size()) {
				res[++tot]=v[i][0].id;
				for(int j=0; j<logm; j++) {
					if(a[res[tot]]&(1<<j)) w[j]=1;
				}
			}
		}
		memset(vis,0,sizeof(vis));
		for(int i=1; i<=tot; i++) {
			printf("%d ",a[res[i]]);
			vis[res[i]]=1;
		}
		for(int i=1; i<=n; i++) {
			if(!vis[i]) printf("%d ",a[i]);
		}
		puts("");
	}
	return 0;
}

CF1741E

考虑 DP,设 \(f_i\) 是以 \(i\) 结尾的序列是否合法。
\(f_{i-1}\) 合法,则 \(f_{i+a_i}\) 合法。
\(f_{i-a_i-1}\) 合法,则 \(f_i\) 合法。

code
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int g,n,a[N];
bool f[N];
int main() {
	scanf("%d",&g);
	for(; g; g--) {
		scanf("%d",&n);
		f[0]=1;
		for(int i=1; i<=n; i++) scanf("%d",&a[i]);
		for(int i=1; i<=n; i++) f[i]=0;
		for(int i=1; i<=n; i++) {
			int p=i-a[i],q=i+a[i];
			if(p>=1) f[i]|=f[p-1];
			if(q<=n) f[q]|=f[i-1];
		}
		puts(f[n]?"yes":"no");
	}
	return 0;
}

CF1739D

由于看到最大值最小,考虑二分。
检验的时候,有两种办法,自上而下和自下而上。
自上而下:从上往下,如果有一个点深度超出限制,则把这个点连到根。
自下而上:从下往上,如果一个点子树深度超过限制,则把子树连到根。
事实证明,自下而上检验是正确的。

code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,tot,head[N],ver[2*N],nxt[2*N],fa[N],k,t;
int cnt,depth[N];
void addedge(int x,int y) {
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
} 
void DFS(int u,int h) {
	depth[u]=1;
	for(int i=head[u]; i; i=nxt[i]) {
		int v=ver[i];
		DFS(v,h);
		depth[u]=max(depth[u],depth[v]+1);
	}
	if(u!=1&&fa[u]!=1&&depth[u]==h) {
		depth[u]=0; cnt++;
	}
}
bool check(int h) {
	cnt=0;
	DFS(1,h);
	return cnt<=k;
}
int main() {
	scanf("%d",&t);
	for(; t; t--) {
		scanf("%d%d",&n,&k);
		for(int i=1; i<=tot; i++) nxt[i]=ver[i]=0;
		for(int i=1; i<=n; i++) head[i]=0;
		tot=0;
		for(int i=2,x; i<=n; i++) {
			scanf("%d",&fa[i]);
			addedge(fa[i],i);
		}
		int l=0,r=n;
		for(; l+1<r; ) {
			int mid=(l+r)/2;
			if(check(mid)) r=mid;
			else l=mid;
		}
		printf("%d\n",r);
	}
	return 0;
}

CF1736D

显然,若将 \(01010101\) 循环交换, 则变成了 \(10101010\).
我们构造 \(a_1,a_3,a_5...a_{2n-1}\)=\(a_2,a_4,a_6...a_{2n}\).

code
// LUOGU_RID: 91283804
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
char s[N];
int n,g,b[N],tot;
int main() {
	scanf("%d",&g);
	for(; g; g--) {
		scanf("%d",&n);
		scanf("%s",s+1);
		tot=0;
		int cnt0=0;
		for(int i=1; i<=2*n; i++) 
			if(s[i]=='0') cnt0++;
		if(cnt0%2!=0) {
			puts("-1");
		} else {
			int m=0;
			for(int i=1; i<=2*n; i+=2) {
				if(s[i]=='0'&&s[i+1]=='1') {
					if(m%2==0) b[++tot]=i;
					else b[++tot]=i+1;
					m++;
				}
				if(s[i]=='1'&&s[i+1]=='0') {
					if(m%2==0) b[++tot]=i+1;
					else b[++tot]=i;
					m++;
				}
			}
			printf("%d ",tot);
			for(int i=1; i<=tot; i++) printf("%d ",b[i]);
			puts("");
			for(int i=1; i<=2*n; i+=2) printf("%d ",i);
			puts("");
		}
	}
	return 0;
}

CF1735D

我们要先求出所有的三元组。
由于每两个数是可以确定出另一个数的,所以五元组一定由两个含有同一元素的三元组构成。
否则,若有两个元素相等,则构成了两个一样的三元组,不构成五元组。
\(O(n^2k)\) 求出所有三元组再匹配即可。

code
#include<bits/stdc++.h>
using namespace std;
const int N=1010,K=22;
typedef long long LL;
int n,k,a[N][K];
LL ans;
map<LL,bool> mp;
int main() {
	scanf("%d%d",&n,&k);
	for(int i=1; i<=n; i++) {
		LL h=0;
		for(int j=1; j<=k; j++) {
			scanf("%d",&a[i][j]);
			a[i][j]++;
			h=h*4+a[i][j];
		}
		mp[h]=1;
	}
	for(int i=1; i<=n; i++) {
		LL cnt=0;
		for(int j=1; j<=n; j++) {
			if(i==j) continue;
			LL h=0;
			for(int p=1; p<=k; p++) {
				if(a[i][p]!=a[j][p]) h=h*4+(6-a[i][p]-a[j][p]);
				else h=h*4+a[i][p];
			}
			if(mp[h]) cnt++;
		}
		cnt/=2;
		ans+=1ll*cnt*(cnt-1)/2;
	}
	printf("%lld\n",ans);
	return 0;
}

CF1728D

考虑 DP,设 \(f_{l,r}\)\(l-r\) 区间内的胜负情况。
1 表示 Alice 胜, 0 表示平局, -1 表示 Bob 胜。
若只剩两个元素,当两元素不同,则 Alice 必胜;反之则平局。这是因为 Alice 会选最优的。
考虑怎么更新。
\(f_{l,r}\),Alice 选左边或右边的元素,希望无论 Bob 怎么选的剩余区间值最大,
Bob 会选左边或右边使剩余区间值最小。
\(f_{l,r}=\max(\min(f_{l+1,r-1},f_{l,r-2}),\min(f_{l+1,r-1},f_{l+2,r}))\).

code
// LUOGU_RID: 91426351
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int t,n,dp[N][N];
char s[N];
int main() {
	scanf("%d",&t);
	for(; t; t--) {
		scanf("%s",s+1);
		n=strlen(s+1);
		for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) dp[i][j]=0;
		for(int i=1; i<n; i++) dp[i][i+1]=(s[i]==s[i+1])?0:1;
		for(int len=4; len<=n; len+=2) {
			for(int i=1; i+len-1<=n; i++) {
				int j=i+len-1;
				int f1=dp[i+2][j];   // A left B left
				if(f1==0&&s[i]<s[i+1]) f1=-1;
				if(f1==0&&s[i]>s[i+1]) f1=1;
				int f2=dp[i+1][j-1]; // A left B right
				if(f2==0&&s[i]<s[j]) f2=-1;
				if(f2==0&&s[i]>s[j]) f2=1;
				int f3=dp[i+1][j-1]; // A right B left
				if(f3==0&&s[j]<s[i]) f3=-1;
				if(f3==0&&s[j]>s[i]) f3=1;
				int f4=dp[i][j-2]; // A right B right
				if(f4==0&&s[j]<s[j-1]) f4=-1;
				if(f4==0&&s[j]>s[j-1]) f4=1;
				dp[i][j]=max(min(f1,f2),min(f3,f4));
			}
		}
		if(dp[1][n]==1) puts("Alice");
		else puts("Draw");
	}
	return 0;
} 
posted @ 2022-10-26 14:44  s1monG  阅读(34)  评论(0编辑  收藏  举报