Loading

CF1305G Kuroni and Antihype

显然最优方案下最后所有人都会加入传销组织,且每个人都是自愿加入或被唯一一个人拉入的。

假设第 \(i\) 个人是由第 \(j\) 个人拉入的,那么考虑连一条边 \(j\to i\),最后就可以得到一个森林,其中根节点为自愿加入的。答案为所有非根节点的父节点权值和。

为了方便,可以设置一个超级根 \(S\),权值为 \(0\),将所有原来的根结点连向 \(S\),这样森林就变成了一棵树。

然后可以发现除 \(S\) 以外的所有相邻的点 \(u,v\) 一定是朋友关系,而 \(S\) 可以和所有点相连。

因此令 \(G\) 为原先的朋友关系图加上 \(S\) 与所有点的边,则最后最优方案为 \(G\) 的一棵生成树。

但是现在有一个问题就是 \((u,v)\) 的权值并不确定(为 \(a_u\)\(a_v\))。

\(f_u\)\(u\) 的父节点,考虑将 \(a_{f_u}\) 拆成 \((a_{f_u}-a_u)+a_u\),而 \(\sum_{u} a_u\) 可以容易求出,而 \((u,v)\) 的权值将固定为 \(a_u+a_v\),这样就只需要求出它的最大生成树即可。

但是直接建图的总边数是 \(\mathcal O(n^2)\) 的,考虑继续优化。

考虑一个叫做 Borůvka 的算法:每一次迭代计算出每个连通块连向其他连通块的边权最大值,然后将他们相连。这样每次能连至少 \(\lceil \frac{n}{2}\rceil\) 条边,在 \(\mathcal O(\log n)\) 次迭代后即可完成。

考虑枚举每个点 \(u\),找出他的朋友中权值最大的 \(v\)。因为 \(a_u\& a_v=0\),所以 \(a_u ⪯¬a_v\),然后现在就是求某个子集中的最大值。

由于迭代次数并不多,所以考虑对每个 \(A\) 求出 \(a_u ⪯ A\) 的最大值。

然后可以发现这就是一个 FMT。

然后就会有一个问题就是在第二轮迭代开始时,有可能会出现当前边权最大的在第一轮已经和它连通。

考虑在维护满足条件的最大顶点 \(v\) 时,顺便维护出与 \(v\) 不在同一联通快的最大顶点 \(v'\),这样答案就一定在 \(v\)\(v'\) 中。

总时间复杂度 \(\mathcal O(a\log a\log n)\)

#include<bits/stdc++.h>
#define R(i,a,b) for(int i=(a),i##E=(b);i<=i##E;i++)
#define L(i,a,b) for(int i=(b),i##E=(a);i>=i##E;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int N=262143;
int n;
ll ans;
int a[222222];
int fa[222222],siz[222222];
pii mx[N+22],to[N+22];
int ff(int x)
{
	return fa[x]==x?x:fa[x]=ff(fa[x]);
}
inline int mer(int x,int y,int opt=0)
{
	if((x=ff(x))==(y=ff(y))) return 1;
	if(opt) siz[x]<siz[y]?(fa[x]=y,siz[y]+=siz[x]):(fa[y]=x,siz[x]+=siz[y]);
	return 0;
}
inline void ckmax(pii &x,int y)
{
	if(a[x.first]<a[y]) 
	{
		if(!mer(x.first,y)) x.second=x.first,x.first=y;
	}
	else if(a[x.second]<a[y]&&!mer(x.first,y)) x.second=y;
}
inline void ckmax(pii &x,const pii &y)
{
	ckmax(x,y.first),ckmax(x,y.second);
}
inline void replace(pii &x,int y,int z)
{
	if(!mer(y,z)&&a[x.first]+a[x.second]<a[y]+a[z]) x={y,z};
}
inline void replace(pii &x,int y,const pii &z)
{
	replace(x,y,z.first),replace(x,y,z.second);
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cin>>n;
	int nn=n+1;
	R(i,1,n) cin>>a[i],ans-=a[i],fa[i]=i,siz[i]=1;	
	a[n+1]=-1;fa[n+1]=n+1;

	while(nn>1)
	{
		R(i,0,N) mx[i]={n+1,n+1};
		R(i,0,n) if(fa[i]==i) to[i]={n+1,n+1};
		R(i,0,n) ckmax(mx[a[i]],i);
		for(int len=1,i=0;i<18;i++,len<<=1)
		{
			for(int j=0;j<=N;j+=len<<1) for(int k=j;k<j+len;k++) ckmax(mx[k+len],mx[k]);
		}
		R(i,0,n) replace(to[ff(i)],i,mx[~a[i]&N]);
		R(i,0,n) if(fa[i]==i&&to[i].second!=n+1&&!mer(i,to[i].second,1)) --nn,ans+=a[to[i].first]+a[to[i].second]; 
	}
	cout<<ans<<endl;
}
posted @ 2022-03-19 00:18  yoisaki_hizeci  阅读(30)  评论(0)    收藏  举报