【比赛记录】2025CSP-S模拟赛28

A B C D Sum Rank
30 10 20 15 75 7/18

A. 路径

看到 DAG,不难想到拓扑排序。考虑在拓扑排序的过程中记录每个点的深度 \(dep\)。不难想到如果有两个点在同一深度,则不合法。但这样的做法不完全。首先每个点可能有多条边指向它,导致它的深度不确定;其次一些错误状态可能被算作正确(如下图)。

A

于是我们只在关键点使深度变大,同时每个点的深度取最大的那个(即从哪个关键点过来),即可判断。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e6+5;
int T,n,m,kk,a[maxn],deg[maxn],dep[maxn];
bool f[maxn],g[maxn];
vector<int> e[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;
		for(int i=1,u,v;i<=m;i++){
			cin>>u>>v;
			e[u].pb(v),deg[v]++;
		}
		cin>>kk;
		for(int i=1;i<=kk;i++){
			cin>>a[i];
			f[a[i]]=1;
		}
		queue<int> q;
		for(int i=1;i<=n;i++){
			if(!deg[i]){
				q.push(i);
				dep[i]=f[i];
			}
		}
		while(q.size()){
			int u=q.front();
			q.pop();
			for(int v:e[u]){
				dep[v]=max(dep[u]+f[v],dep[v]);
				if(--deg[v]==0){
					q.push(v);
				}
			}
		}
		for(int i=1;i<=kk;i++){
			if(g[dep[a[i]]]){
				cout<<"No\n";
				goto togo;
			}
			g[dep[a[i]]]=1;
		}
		cout<<"Yes\n";
		togo:;
		for(int i=1;i<=n;i++){
			deg[i]=dep[i]=f[i]=g[i]=0;
			e[i].clear();
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

B. 异或

区间操作不好处理,考虑令 \(d_i=a_i\oplus a_{i-1}\),于是一次区间操作变为单点操作或双点操作,目标仍为使数组归零。将每次操作的两点连边,答案即为总边数最小值。考虑一个大小为 \(x\) 的连通块,发现其边数最大为 \(x\)(基环树显然成立),最小为 \(x-1\)(树)。取得最小值的充要条件是该连通块异或和为零,证明较为简单。于是我们希望最大化这样的连通块个数,设其为 \(m\),答案即为 \(n-m\)。状压 DP 即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define popcnt __builtin_popcount
using namespace std;
namespace asbt{
const int maxn=(1<<17)+5;
int n,dp[maxn];
ll a[20],sum[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=n;i;i--){
		a[i]^=a[i-1];
	}
	for(int S=0;S<1<<n;S++){
		for(int i=1;i<=n;i++){
			if(S>>(i-1)&1){
				sum[S]^=a[i];
			}
		}
	}
	int U=(1<<n)-1;
	for(int S=0;S<1<<n;S++){
		int nS=U^S;
		for(int T=nS;T;T=(T-1)&nS){
			if(!sum[T]){
				dp[S|T]=max(dp[S|T],dp[S]+1);
			}
		}
	}
	cout<<n-dp[U];
	return 0;
}
}
int main(){return asbt::main();}

C. 距离

首先考虑特殊性质 A,即每次只插入/查询一个点,可以用点分树解决,因为有 \(w\ge 1\) 所以可以直接点分树。

当引入点对时,我们离线下来,再套一层点分治即可。具体地,设当前的分治中心为 \(u\),对于修改操作 \((a,b)\),在点分树上的每个点 \(v\) 维护 \(val_v=\min\{\operatorname{dis}(a,u)+\operatorname{dis}(b,v)\}\)。那么对于查询 \((x,y)\),我们只需要求出 \(\min\{\operatorname{dis}(x,u)+val_v+\operatorname{dis}(y,v)\}\),更新答案即可。时间复杂度 \(O(n\log^2n)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace asbt{
const int maxn=2e5+5;
const ll inf=1e18;
int n,m,rt,tot,fa[maxn],sz[maxn],mxs[maxn];
ll ans[maxn],cun[maxn];
bool ban[maxn];
vector<int> E[maxn],Q[maxn];
vector<pii> e[maxn];
struct{
	int opt,a,b;
}q[maxn];
namespace LCA{
	int cnt,dfn[maxn],oula[maxn<<1],idx[maxn<<1];
	ll dep[maxn];
	il void dfs(int u,int fa){
		dfn[u]=++cnt;
		oula[cnt]=cnt,idx[cnt]=u;
		for(pii i:e[u]){
			int v=i.fir,w=i.sec;
			if(v==fa){
				continue;
			}
			dep[v]=dep[u]+w;
			dfs(v,u);
			oula[++cnt]=dfn[u];
		}
	}
	struct{
		int st[20][maxn<<1],Log[maxn<<1];
		il void build(){
			for(int i=2;i<=cnt;i++){
				Log[i]=Log[i>>1]+1;
			}
			for(int i=1;i<=cnt;i++){
				st[0][i]=oula[i];
			}
			for(int j=1;j<=Log[cnt];j++){
				for(int i=1;i+(1<<j)-1<=cnt;i++){
					st[j][i]=min(st[j-1][i],st[j-1][i+(1<<(j-1))]);
				}
			}
		}
		il int query(int l,int r){
			int p=Log[r-l+1];
			return min(st[p][l],st[p][r-(1<<p)+1]);
		}
	}ST;
	il void init(){
		dfs(1,0),ST.build();
	}
	il int lca(int u,int v){
		if(dfn[u]>dfn[v]){
			swap(u,v);
		}
		return idx[ST.query(dfn[u],dfn[v])];
	}
	il ll dis(int u,int v){
		return dep[u]+dep[v]-2*dep[lca(u,v)];
	}
}
using LCA::dis;
il void dfs1(int u,int fa){
	tot++;
	for(pii i:e[u]){
		int v=i.fir;
		if(v==fa||ban[v]){
			continue;
		}
		dfs1(v,u);
	}
}
il int dfs2(int u,int fa){
	sz[u]=1,mxs[u]=0;
	int mnp=0;
	ll mns=inf;
	for(pii i:e[u]){
		int v=i.fir;
		if(v==fa||ban[v]){
			continue;
		}
		int t=dfs2(v,u);
		if(mxs[t]<mns){
			mns=mxs[t],mnp=t;
		}
		sz[u]+=sz[v];
		mxs[u]=max(mxs[u],sz[v]);
	}
	mxs[u]=max(mxs[u],tot-sz[u]);
	return mns<mxs[u]?mnp:u;
}
il int build(int u){
//	cout<<u<<" ";
	tot=0,dfs1(u,0);
//	cout<<tot<<"\n";
	int x=dfs2(u,0);
	ban[x]=1;
	for(pii i:e[x]){
		int v=i.fir;
		if(ban[v]){
			continue;
		}
		int y=build(v);
		fa[y]=x,E[x].pb(y);
	}
	return x;
}
il void solve(int u){
//	cout<<u<<":\n";
	for(int i:Q[u]){
//		cout<<i<<" ";
		int opt=q[i].opt,a=q[i].a,b=q[i].b;
		if(opt==1){
			int x=b;
			while(x){
				cun[x]=min(cun[x],dis(x,b)+dis(a,u));
				x=fa[x];
			}
		}
		else{
			int x=b;
			while(x){
				ans[i]=min(ans[i],dis(a,u)+cun[x]+dis(b,x));
				x=fa[x];
			}
		}
	}
//	cout<<"\n";
	for(int i:Q[u]){
		int opt=q[i].opt,b=q[i].b;
		if(opt==1){
			int x=b;
			while(x){
				cun[x]=inf,x=fa[x];
			}
		}
	}
	for(int v:E[u]){
		solve(v);
	}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1,u,v,w;i<n;i++){
		cin>>u>>v>>w;
		e[u].pb(mp(v,w));
		e[v].pb(mp(u,w));
	}
	LCA::init();
//	for(int u=1;u<=n;u++){
//		for(int v=1;v<=n;v++){
//			cout<<u<<" "<<v<<" "<<dis(u,v)<<"\n";
//		}
//	}
	rt=build(1);
//	cout<<rt<<"\n";
//	for(int u=1;u<=n;u++){
//		for(int v:E[u]){
//			cout<<u<<" "<<v<<"\n";
//		}
//	}
	for(int i=1;i<=m;i++){
		cin>>q[i].opt>>q[i].a>>q[i].b;
		int u=q[i].a;
		while(u){
			Q[u].pb(i);
			u=fa[u];
		}
	}
	memset(cun,0x3f,sizeof(cun));
	memset(ans,0x3f,sizeof(ans));
	solve(rt);
	for(int i=1;i<=m;i++){
		if(q[i].opt==2){
			cout<<(ans[i]>=inf?-1:ans[i])<<"\n";
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

D. 花之舞

posted @ 2025-07-28 19:33  zhangxy__hp  阅读(25)  评论(0)    收藏  举报