【比賽】暑假集訓CSP提高模擬4

萝卜甜了發表於2024-07-26

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;
}

相關文章