图论练习
CF888G Xor-MST#
题意:
- 给定 个结点的无向完全图。每个点有一个点权为 。连接 号结点和 号结点的边的边权为 。
- 求这个图的
最小生成树
的权值。 - ,。
思路:
看到异或,想到 01trie
和 线性基
线性基明显不大行,考虑 01trie
先将每个节点的权值插入 中,如下图
容易发现,若是两个点连边,权值就是 以下的边的异或值
考虑深度最大的 ,可以发现,若是 两两不同,那么有且仅有 个这样的
最好的结果就是将这 个 以下的边的值贪心的算出来
贪心:尽量让高位的 相同,使得两个值 的结果尽量小
一遍找到所有有两个儿子的节点搞贪心即可
#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
,然后把最小生成树内的所有边都标记一下
枚举所有没有标记的边,考虑用当前边权 替换最小生成树内的边
我们用 表示链 上边权最大的边, 表示第二大的
若是要替换树内 的边,那么只有两种情况
- ,则令
- ,则令
这样再计算一下,并更新答案
和 用树链剖分维护即可
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
2020-07-06 浅谈树状数组