【比赛】暑假集训CSP提高模拟4

T1 White and Black 0Pts

原题 Lights Out on Tree

显然不存在无解情况,因为可以从根开始 dfs,遇到黑点就翻转。

所以每个点最多只会翻转一次,且与顺序无关,所以翻转方式只有一种。

有多测,不能暴力模拟。
手玩一下可以发现如果 \(col_u\)\(col_{fa_u}\) 不同就会贡献一次翻转;
简单处理即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,q,fa[N];
int x,a[N],col[N];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>q;
	for(int i=2;i<=n;i++){
		cin>>fa[i];
		col[fa[i]]++;
	}
	while(q--){
		cin>>x;
		int ans=x;
		for(int i=1;i<=x;i++){
			cin>>a[i];
			col[fa[a[i]]]-=2;
		}
		for(int i=1;i<=x;i++){
			ans+=col[a[i]];
		}
		for(int i=1;i<=x;i++){
			col[fa[a[i]]]+=2;
		}
		cout<<ans<<"\n";
	}
	return 0;
}

T2 White and White 0Pts

原题 Encryption (hard)

首先考虑暴力。复杂度 \(O(n^2k)\),对应 Easy Vision;

然后发现 \(k\) 很小,可以通过枚举模数的位置对应的 dp 最小值,可以做到 \(O(nkp)\),对应 Medium Vision;

考虑优化最初的暴力 \(O(n^2k)\),发现 \(f_{i,j} \equiv \pmod p\),于是可以直接记录 \(f_{*,j}\) 的最小值进行转移,复杂度 \(O(nk)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n,k,p,f[N][101];
int g[2][101];
int sum[N];
int main(){
	cin>>n>>k>>p;
	for(int i=1;i<=n;i++){
		cin>>sum[i];
		sum[i]=(sum[i]+sum[i-1])%p;
	}
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=k;j++){
			int lst=g[i-1&1][j-1];
			f[i][j]=min(f[i][j],f[lst][j-1]+(sum[i]-sum[lst]+p)%p);
		}
		for(int j=0;j<=k;j++){
			g[i&1][j]=g[i-1&1][j];
			if(f[i][j]<f[g[i&1][j]][j])g[i&1][j]=i;
		}
	}
	cout<<f[n][k];
	return 0;
}

T3 Black and Black 10Pts

原题 ± Increasing Sequence

首先构造一个 \(1\) ~ \(n\) 的排列,然后算出此时的 \(s\)
如果此时 \(s=0\) 直接输出就行了;
如果 \(s>0\),考虑寻找一个为正的前缀或者一个为负的后缀,并将其加上或减去 \(s\) 即可;反之则一定无解。
\(s<0\) 的情况同理。

点击查看代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,a[N],s,sum[N],mus[N];
void change(int st,int x,int op){
	cout<<"Yes\n";
	if(op){
		for(int i=1;i<=n;i++){
			if(i>=st)cout<<i+x<<" ";
			else cout<<i<<" ";
		}
	}
	else{
		for(int i=1;i<=n;i++){
			if(i<=st)cout<<i-x<<" ";
			else cout<<i<<" ";
		}
	}
}
main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s+=a[i]*i;
	}
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+a[i];
	}
	for(int i=n;i>=1;i--){
		mus[i]=mus[i+1]+a[i];
	}
	if(s==0)return change(0,0,1),0;
	if(s>0){
		int p=0,f=0;
		for(int i=1;i<=n;i++){
			if(sum[i]==1)p=i;
			if(mus[i]==-1)f=i;
		}
		if(!p&&!f)return cout<<"No\n",0;
		if(p)change(p,s,0);
		else change(f,s,1);
	}
	else{
		int p=0,f=0;
		for(int i=1;i<=n;i++){
			if(sum[i]==-1)p=i;
			if(mus[i]==1)f=i;
		}
		if(!p&&!f)return cout<<"No\n",0;
		if(p)change(p,-s,0);
		else change(f,-s,1);
	}
	return 0;
}

T4 Black and White 60Pts

原题 捉迷藏

括号序列,因为好贺

大体意思是在 DFS 序中加入括号来表示树的结构,此时两个点间消去匹配括号的括号数量即为两点间距离;

然后就又是一道山海经。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5;
struct edge{
	int next,to;
}e[N];
int h[N],cnt;
void add(int u,int v){
	e[++cnt]={h[u],v};
	h[u]=cnt;
}
int n,q,x;
char ch;
int s[N*3],tot,num;
int in[N];
bool c[N];
void dfs(int x,int fa){
	s[++tot]=-1;
	s[++tot]=x;
	in[x]=tot;
	for(int i=h[x];i;i=e[i].next){
		int to=e[i].to;
		if(to==fa)continue;
		dfs(to,x);
	}
	s[++tot]=-2;
}
struct tree{
	int a,b;
	int l1,l2;
	int r1,r2;
	int dis;
}t[N<<2];
void init(int k,int x){
	t[k].a=t[k].b=0;
	t[k].l1=t[k].l2=t[k].r1=t[k].r2=t[k].dis=-1e9;
	if(s[x]==-1)t[k].b=1;
	else if(s[x]==-2)t[k].a=1;
	else if(!c[s[x]])t[k].l1=t[k].r1=t[k].l2=t[k].r2=0;
}
void pushup(int k){
	if(t[k<<1].b>t[k<<1|1].a){
		t[k].a=t[k<<1].a;
		t[k].b=t[k<<1].b-t[k<<1|1].a+t[k<<1|1].b;
	}
	else{
		t[k].b=t[k<<1|1].b;
		t[k].a=t[k<<1|1].a-t[k<<1].b+t[k<<1].a;
	}
	t[k].l1=max({t[k<<1].l1,t[k<<1|1].l1+t[k<<1].a-t[k<<1].b,t[k<<1|1].l2+t[k<<1].a+t[k<<1].b});
	t[k].l2=max(t[k<<1].l2,t[k<<1|1].l2-t[k<<1].a+t[k<<1].b);
	t[k].r1=max({t[k<<1|1].r1,t[k<<1].r1+t[k<<1|1].b-t[k<<1|1].a,t[k<<1].r2+t[k<<1|1].a+t[k<<1|1].b});
	t[k].r2=max(t[k<<1|1].r2,t[k<<1].r2+t[k<<1|1].a-t[k<<1|1].b);
	t[k].dis=max({t[k<<1].r1+t[k<<1|1].l2,t[k<<1].r2+t[k<<1|1].l1,t[k<<1].dis,t[k<<1|1].dis});
}
void build(int k,int l,int r){
	if(l==r){
		init(k,l);
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
void update(int k,int l,int r,int pos){
	if(l==r){
		init(k,l);
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)update(k<<1,l,mid,pos);
	else update(k<<1|1,mid+1,r,pos);
	pushup(k);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1,x,y;i<n;i++){
		cin>>x>>y;
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	num=n;
	build(1,1,tot);
	cin>>q;
	while(q--){
		cin>>ch;
		if(ch=='C'){
			cin>>x;
			num+=c[x]?1:-1;
			c[x]^=1;
			update(1,1,tot,in[x]);
		}	
		else{
			if(num==0)cout<<"-1\n";
			else if(num==1)cout<<"0\n";
			else cout<<t[1].dis<<"\n";
		}
	}
	return 0;
}
posted @ 2024-07-26 06:27  萝卜甜了  阅读(24)  评论(0编辑  收藏  举报