CF1508C tj

CF1508C

你总不能一下午啥事情都没干吧。

题面

作为一名教师,Riko Hakozaki 经常需要帮助她的学生解决各类学科的问题。今天,她被问到了一个编程任务,内容如下:

给定一个无向完全图,包含 \(n\) 个节点,其中部分边已经被赋予了正权值,其余边尚未赋值。你需要为所有未赋值的边分配非负权值,使得最终所有边都被赋值后的完全图中,所有边权的异或和等于 \(0\)

定义一个完全赋值的完全图的“丑陋度”为其最小生成树的权值,即最小生成树所有边权之和。你需要合理分配权值,使得最终图的丑陋度尽可能小。

提示:一个无向完全图有 \(n\) 个节点,包含所有 \(1 \le u < v \le n\) 的边;这样的图共有 \(\frac{n(n-1)}{2}\) 条边。

她不确定如何解决这个问题,因此请你帮她解决。

sol

又是分讨题。

设原来边权的异或值为 \(S\)

发现如果我们想要让这个最小生成树尽量小的话,有一个简单的做法是只定一条边为 \(S\),剩下全填 \(0\),感性理解一下会发现这个是对的。

在未赋值的边联通且有环的情况下答案一定为 \(0\)

接下来就有两种情况需要讨论了:

补图刚好是一棵树。

这种情况点数不会太多,暴力枚举哪条边改为 \(S\) 跑最小生成树就行了。

直接做就是 \(O(n^3\log n^2)\) 的。

补图不连通

这个时候也得分两种情况,看有没有环。

有环我直接把补图连通块跑出来搞最小生成树就行了,那个边权为 \(S\) 的边随便找个环放了就好了。

没环的时候也可以说明点数不会太多,也是枚举哪条边放就完事了,也是 \(O(n^3\log n^2)\)

代码还没写,但是这个能难写到哪去???

实现需要精细实现 (有点搞人)

比如算补图为森林/树的最小生成树需要通过先排序把他的边数降成 \(O(n)\) 的再跑。

以及算补图连通块,你先找到度数最小的一个点,那么这个连通块至少包含 \(n-m/n\) 个点,然后剩下的 \(m/n\) 暴力算就行了,均摊是 \(O(n)\) 的复杂度。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,chs[200001],v[200001],fa[200001],vis[701][701],vv[200001],viv[200001],tot,pnt,vl;
string s;
struct dcz{
	int x,y,z;
}a[200001],b[400001],c[400001],d[400001];
bool cmp(dcz x,dcz y){
	return x.z<y.z;
}
int gf(int x){
	if(x==fa[x]) return x;
	return fa[x]=gf(fa[x]);
}
struct sbdcz{
	int nex,to;
}e[400001];
int cnt,head[200001],du[200001];
void add(int x,int y){
	e[++cnt].nex=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void solve2(){//21í?2?á?í¨óD?·?£ 
//	cout<<"?\n";
	for(int i=1;i<=m;i++){
		du[a[i].x]++,du[a[i].y]++;
		add(a[i].x,a[i].y);
		add(a[i].y,a[i].x);
	}
	int id=1;
	for(int i=1;i<=n;i++){
		fa[i]=i;
		if(du[id]>du[i]) id=i;
	}
	for(int i=head[id];i;i=e[i].nex){
		int v=e[i].to;
		vv[v]=1;
	}
	for(int i=1;i<=n;i++){
		if(!vv[i]) fa[i]=id;
	}
	for(int u=1;u<=n;u++){
		if(!vv[u]||u==id) continue;
		for(int i=1;i<=n;i++){
			viv[i]=0;
		}
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].to;
			viv[v]=1;
		}
		int fu=gf(u);
		for(int i=1;i<=n;i++){
			if(!viv[i]){
				int fx=gf(i);
				fa[fx]=fu;
			}
		}
	}
	sort(a+1,a+m+1,cmp);
	int ans=0;
	for(int i=1;i<=m;i++){
		int fx=gf(a[i].x),fy=gf(a[i].y);
		if(fx!=fy){
			ans+=a[i].z;
			fa[fy]=fx;
		}
	}
	cout<<ans<<"\n";
}
void solve1(){
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=1;i<=m;i++){
		vis[a[i].x][a[i].y]=1;
		int fx=gf(a[i].x),fy=gf(a[i].y);
		if(fx!=fy){
			fa[fy]=fx;
			d[++tot]=a[i];
		}
	}
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	bool ff=0;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(vis[i][j]||vis[j][i]) continue;
			int fx=gf(i),fy=gf(j);
			if(fx==fy){ff=1;continue;}
			fa[fy]=fx;
			b[++pnt]={i,j,0};
		}
	}
	int af=gf(1);bool f=1;
	for(int i=1;i<=n;i++){
		if(gf(i)!=af){f=0;break;}
	}
	if(f&&ff){ cout<<0<<"\n";return;}
	else if(!ff){
		int ans=LLONG_MAX;
		for(int i=1;i<=pnt;i++){
			b[i].z=vl;
			for(int j=1;j<=n;j++){
				fa[j]=j;
			}
			for(int j=1;j<=tot+pnt;j++){
				if(j<=tot) c[j]=d[j];
				else c[j]=b[j-tot];
//				cout<<c[j].x<<' '<<c[j].y<<' '<<c[j].z<<"\n";
			}
			sort(c+1,c+tot+pnt+1,cmp);
			int val=0,pnp=0;
			for(int j=1;j<=tot+pnt;j++){
				int fx=gf(c[j].x),fy=gf(c[j].y);
				if(fx!=fy){
					val+=c[j].z;pnp++;fa[fy]=fx;
					if(pnp==n-1) break;
				}
			}
//			cout<<val<<"\n";
			ans=min(ans,val);
			b[i].z=0;
		}
		cout<<ans<<"\n";return;
	}
	else solve2();
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>a[i].x>>a[i].y>>a[i].z;
		vl^=a[i].z;
	}
	if(n<=700) solve1();
	else solve2();
	return 0;
}
posted @ 2025-10-22 17:20  degchuzm  阅读(9)  评论(0)    收藏  举报