Loading

图论练习

CF888G Xor-MST

题意

  • 给定 \(n\) 个结点的无向完全图。每个点有一个点权为 \(a_i\)。连接 \(i\) 号结点和 \(j\) 号结点的边的边权为 \(a_i\oplus a_j\)
  • 求这个图的 最小生成树 的权值。
  • \(1\le n\le 2\times 10^5\)\(0\le a_i< 2^{30}\)

思路

看到异或,想到 01trie线性基

线性基明显不大行,考虑 01trie

先将每个节点的权值插入 \(trie\) 中,如下图

容易发现,若是两个点连边,权值就是 \(lca\) 以下的边的异或值

考虑深度最大的 \(lca\),可以发现,若是 \(a_i\) 两两不同,那么有且仅有 \(n-1\) 个这样的 \(lca\)

最好的结果就是将这 \(n-1\)\(lca\) 以下的边的值贪心的算出来

贪心:尽量让高位的 \(1/0\) 相同,使得两个值 \(xor\) 的结果尽量小

\(dfs\) 一遍找到所有有两个儿子的节点搞贪心即可

#include<bits/stdc++.h>
using namespace std;

const int inf=1<<30;
const int N=6e6+5;

#define ll long long

int n,tot;
int t[N][2];
ll ans;

inline void insert(int x){
	int rt=0;
	for(int i=30;i>=0;--i){
		int c=(1<<i)&x?1:0;
		if(!t[rt][c]) t[rt][c]=++tot;
		rt=t[rt][c];
	}
}

inline ll get_mn(int l,int r,int dep){
	if(dep<0) return 0;
	ll a1=inf,a2=inf;
	if(t[l][0]&&t[r][0]) a1=get_mn(t[l][0],t[r][0],dep-1);
	if(t[l][1]&&t[r][1]) a2=get_mn(t[l][1],t[r][1],dep-1);
	if(a1!=inf||a2!=inf) return min(a1,a2);
	if(t[l][0]&&t[r][1]) a1=get_mn(t[l][0],t[r][1],dep-1)+(1<<dep);
	if(t[l][1]&&t[r][0]) a2=get_mn(t[l][1],t[r][0],dep-1)+(1<<dep);
	return min(a1,a2);
}

inline void dfs(int rt,int dep){
	if(dep<0) return;
	if(t[rt][0]&&t[rt][1]) ans+=get_mn(t[rt][0],t[rt][1],dep-1)+(1<<dep);
	if(t[rt][0]) dfs(t[rt][0],dep-1);
	if(t[rt][1]) dfs(t[rt][1],dep-1);
}

signed main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1,x;i<=n;++i){
		cin>>x;
		insert(x);
	}
	dfs(0,30);
	cout<<ans<<endl;
}

P4180 严格次小生成树

题意

给你一个无向图,求其严格最小生成树

思路

先跑一遍 Kruskal,然后把最小生成树内的所有边都标记一下

枚举所有没有标记的边,考虑用当前边权 \(v\) 替换最小生成树内的边

我们用 \(mx\) 表示链 \(x\sim y\) 上边权最大的边,\(smx\) 表示第二大的

若是要替换树内 \(x\sim y\) 的边,那么只有两种情况

  • \(v>mx\),则令 \(v=mx\)
  • \(v=mx\),则令 \(v=smx\)

这样再计算一下,并更新答案

\(mx\)\(smx\) 用树链剖分维护即可

#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}

const int N=1e6+5;
const int M=1e6+5;
const long long inf=1e18;

#define pii pair <int,int>
#define mp make_pair
#define lsp p<<1
#define rsp p<<1|1
#define size awefdaw
#define int long long

struct edge{
	int x,y,z;
	bool operator < (const edge X) const{
		return z<X.z;
	}
}e[M];

int n,m,tot,sum,ans=inf;
int g[N];
bool vis[N];
int son[N],f[N],size[N],dep[N];
int dfn[N],top[N],val[N],w[N];
int cnt;
vector <pii> G[N];

inline void add(int x,int y,int z){
	G[x].push_back(mp(y,z));
}

inline int get(int x){
	return x==g[x]?x:g[x]=get(g[x]);
}

inline void kruskal(){
	sort(e+1,e+m+1);
	int num=0;
	for(int i=1;i<=n;++i) g[i]=i;
	for(int i=1;i<=m;++i){
		int fx=get(e[i].x),fy=get(e[i].y);
		if(fx==fy) continue;
		g[fy]=fx;
		add(e[i].x,e[i].y,e[i].z);
		add(e[i].y,e[i].x,e[i].z);
		vis[i]=1;
		sum+=e[i].z,++num;
		if(num==n-inf) break;
	}
}

struct node{
	int mx,smx;
	int l,r;
}t[N<<2];

inline int work(int a,int b,int c,int d){
	int h[4]={a,b,c,d};
	sort(h,h+4);
	for(int i=2;i>=0;--i)
		if(h[i]!=h[3]) return h[i];
	return -inf;
}

node operator + (node l,node r){
	node x=(node){-inf,-inf,l.l,r.r};
	x.mx=max(l.mx,r.mx);
	x.smx=work(l.mx,r.mx,l.smx,r.smx);
	return x;
}

inline void build(int p,int l,int r){
	if(l==r){
		t[p]=(node){val[l],val[l],l,r};
		return;
	}
	int mid=l+r>>1;
	build(lsp,l,mid);
	build(rsp,mid+1,r);
	t[p]=t[lsp]+t[rsp];
}

inline node query(int p,int l,int r){
	if(l<=t[p].l&&t[p].r<=r) return t[p];
	int mid=t[p].l+t[p].r>>1;
	node res=(node){-inf,-inf,0,0};
	if(l<=mid) res=res+query(lsp,l,r);
	if(mid<r) res=res+query(rsp,l,r);
	return res;
}

inline void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,size[x]=1,f[x]=fa;
	int maxson=-inf;
	for(auto y:G[x]){
		int a=y.first,b=y.second;
		if(a==fa) continue;
		dfs1(a,x);
		w[a]=b;
		size[x]+=size[a];
		if(size[a]>maxson) son[x]=a,maxson=size[a];
	}
}

inline void dfs2(int x,int tp){
	top[x]=tp,dfn[x]=++cnt,val[cnt]=w[x];
	if(!son[x]) return;
	dfs2(son[x],tp);
	for(auto y:G[x]){
		int a=y.first;
		if(a!=son[x]&&a!=f[x]) dfs2(a,a);
	}
}

inline node Q(int x,int y){
	node res=(node){-inf,-inf,0,0};
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res=res+query(1,dfn[top[x]],dfn[x]);
		x=f[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	return res+query(1,dfn[x]+1,dfn[y]);
}

signed main(){
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		int x=read(),y=read(),z=read();
		e[i]=(edge){x,y,z};
	}
	kruskal();
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	for(int i=1;i<=m;++i)
		if(!vis[i]){
			node res=Q(e[i].x,e[i].y);
			int now=sum;
			if(res.mx!=e[i].z) now=now-res.mx+e[i].z;
			else if(res.smx!=-inf) now=now-res.smx+e[i].z;
			if(now!=sum) ans=min(ans,now);
		}
	cout<<ans;
}
posted @ 2022-07-06 11:28  Into_qwq  阅读(16)  评论(0编辑  收藏  举报