图论练习
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;
}