Problem
]
Solution
可以发现,若将武士之间的决斗了的人连一条边,可以构成一颗树,边权为 a u ⨁ a v a_u \bigoplus a_v au⨁av
同时要求边权最小,即最小生成树,但暴力跑出每条边是 n 2 n^2 n2的,所以考虑用 T r i e Trie Trie来优化。
首先有个贪心思想:若 a i = a j a_i = a_j ai=aj,则 i , j i,j i,j连边,接着按二进制位来贪心连边。
所以考虑建一颗 T r i e Trie Trie,从叶子节点开始合并联通块,对于 T r i e Trie Trie的某个节点的两个儿子所在的
联通块内找一条权值最小的边连接即可。
p.s.
mmp考试时sb一般地先开了一颗 T r i e Trie Trie,然后又动态开点合并 T r i e Trie Trie, R E + M L E RE+MLE RE+MLE,%$#@^#!@!!!
结果: 100 − > 50 100 -> 50 100−>50
Code
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月07日 星期一 09时01分15秒
*******************************/
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
struct edge{
signed to,next;
int w;
edge(int v=0,int nx=0,int f=0):to(v),next(nx),w(f){}
bool operator<(const edge & p) const
{
return w<p.w;
}
};
const signed maxn=1e5+10;
const int LG=20;
const int Lg=60;
const int inf=1e18;
const int MI=1ll<<Lg;
signed n,m,head[maxn],cnt,u,v,tot,depth[maxn],f[maxn][LG+1];
int a[maxn],ans,w,mx[maxn][LG+1];
edge e[maxn<<1];
void add(int u,int v,int w=0)
{
e[++cnt]=edge(v,head[u],w);
head[u]=cnt;
}
namespace Trie{
signed ch[maxn*70][2],id[maxn*140],tot=1;
void insert(int x,int _id)
{
int c,now=1;
for(int i=Lg;i>=0;i--)
{
c=(x&(1ll<<i))>>i;
if(!ch[now][c]) ch[now][c]=++tot;
now=ch[now][c];
}
if(!id[now])
id[now]=_id;
else
{
add(_id,id[now]);
add(id[now],_id);
}
}
void query(int x,int y,int val,int mi)
{
if(val>w)return;
if(w==0)return;
if(!mi)
{
if(w>val)
{
w=val,u=id[x],v=id[y];
}
return;
}
if(ch[x][0] && ch[y][0])
query(ch[x][0],ch[y][0],val,mi>>1);
if(ch[x][1] && ch[y][1])
query(ch[x][1],ch[y][1],val,mi>>1);
if(ch[x][0] && ch[y][1])
query(ch[x][0],ch[y][1],val|mi,mi>>1);
if(ch[x][1] && ch[y][0])
query(ch[x][1],ch[y][0],val|mi,mi>>1);
}
void solve(int now,int val,int mi)
{
if(!ch[now][0] && !ch[now][1])
return;
if(ch[now][0]) solve(ch[now][0],val,mi>>1);
if(ch[now][1]) solve(ch[now][1],val|mi,mi>>1);
w=inf;
if(ch[now][0] && ch[now][1])
{
query(ch[now][0],ch[now][1],mi,mi>>1);
add(u,v,w);
add(v,u,w);
ans+=w;
}
}
};
void dfs(int now)
{
depth[now]=depth[f[now][0]]+1;
for(int i=1;i<=LG;i++)
f[now][i]=f[f[now][i-1]][i-1],mx[now][i]=max(mx[now][i-1],mx[f[now][i-1]][i-1]);
for(int i=head[now];i;i=e[i].next)
if(e[i].to!=f[now][0])
{
f[e[i].to][0]=now;
mx[e[i].to][0]=e[i].w;
dfs(e[i].to);
}
}
int LCA(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
int res=-1;
for(int i=LG;i>=0;i--)
if(depth[f[x][i]]>=depth[y])
res=max(res,mx[x][i]),x=f[x][i];
if(x==y) return res;
for(int i=LG;i>=0;i--)
if(f[x][i]!=f[y][i])
res=max(res,max(mx[x][i],mx[y][i])),x=f[x][i],y=f[y][i];
return max(res,max(mx[x][0],mx[y][0]));
}
signed main()
{
//freopen("fight.in","r",stdin);
//freopen("fight.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i],Trie::insert(a[i],i);
Trie::solve(1,0,MI);
printf("%lld\n",ans);
dfs(1);
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
int res=(a[u]^a[v])&w;
int tmp=LCA(u,v);
printf("%lld\n",ans+min(res-tmp,0ll));
}
return 0;
}