对整个过程构造一张有向图,其中$(x,y)in E$当且仅当$x$把$y$加入,且边权为$a_{x}$
显然这是一棵外向树森林,并再做如下两个构造:
1.新建一个点$a_{0}=0$,将其向所有入度为0的点连边
2.将所有边变为无向边,且边权修改为$a_{x}+a_{y}$($x$和$y$为两端点)
显然最终得到的是一棵树,并且这棵树能被某个过程得到当且仅当$forall (x,y)in E,a_{x}and a_{y}=0$
另一方面,初始答案即边权和,第一个构造中新增的边边权为0,第二个构造中即每一个点都额外产生了原来入度次贡献,而除了0以外其余点入度均为1(而$a_{0}=0$不需要考虑),那么最终答案即边权和-$sum_{i=1}^{n}a_{i}$
由于后者固定,问题也即求$E={(x,y,a_{x}+a_{y})mid 0le x,yle n$且$a_{x}and a_{y}=0}$的最大生成树
考虑Boruvka算法,即维护若干个集合(初始每一个点均作为一个集合),并不断加入所有集合(向集合外)最大的出边,注意到每一次集合数回减小一半,最终轮数即$o(log n)$
在每一轮中,需要求出该最大出边,不妨对每一个点都求出该最大出边即可
而对于$a_{x}$,即需要求出权值是$Uoplus a_{x}$的子集(其中$U$为全集)中与$x$不在同一个连通块中且权值最大的点,对每一个集合预处理其子集权值最大的和不在同一个连通块中权值次大的即可
关于这个,做一个类似于高维前缀和的操作即可
时间复杂度为$o(nalpha(n)+m2^{m}log n)$(其中$mapprox 18$),可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 #define M 18 5 #define ll long long 6 #define pii pair<int,int> 7 #define fi first 8 #define se second 9 struct Data{ 10 pii mx,cmx; 11 }g[1<<M]; 12 int n,a[N],fa[N]; 13 ll ans; 14 pii f[N]; 15 int find(int k){ 16 if (k==fa[k])return k; 17 return fa[k]=find(fa[k]); 18 } 19 void merge(int x,int y,int z){ 20 x=find(x),y=find(y); 21 if (x!=y){ 22 fa[x]=y; 23 ans+=z; 24 } 25 } 26 Data merge(Data x,Data y){ 27 if (x.mx<y.mx)swap(x,y); 28 if (find(x.mx.se)==find(y.mx.se))swap(y.mx,y.cmx); 29 x.cmx=max(x.cmx,y.mx); 30 return x; 31 } 32 int main(){ 33 scanf("%d",&n); 34 for(int i=1;i<=n;i++){ 35 scanf("%d",&a[i]); 36 ans-=a[i]; 37 } 38 for(int i=0;i<=n;i++)fa[i]=i; 39 while (1){ 40 bool flag=0; 41 for(int i=1;i<=n;i++) 42 if (find(i)!=find(0)){ 43 flag=1; 44 break; 45 } 46 if (!flag)break; 47 for(int i=0;i<(1<<M);i++)g[i].mx=g[i].cmx=make_pair(-1,-1); 48 for(int i=0;i<=n;i++)g[a[i]]=merge(g[a[i]],Data{make_pair(a[i],i),make_pair(-1,-1)}); 49 for(int i=0;i<M;i++) 50 for(int j=0;j<(1<<M);j++) 51 if (j&(1<<i))g[j]=merge(g[j],g[j^(1<<i)]); 52 for(int i=0;i<=n;i++)f[i]=make_pair(-1,-1); 53 for(int i=0;i<=n;i++){ 54 int pos=(a[i]^((1<<M)-1)); 55 pii o=g[pos].mx; 56 if (o.fi>=0){ 57 if (find(i)==find(o.se))o=g[pos].cmx; 58 if (o.fi>=0)f[find(i)]=max(f[find(i)],make_pair(o.fi+a[i],o.se)); 59 } 60 } 61 for(int i=0;i<=n;i++) 62 if (f[i].fi>=0)merge(i,f[i].se,f[i].fi); 63 } 64 printf("%lld ",ans); 65 return 0; 66 }