[APIO2013]道路费用

II.[APIO2013]道路费用

这个(k),明显就很可以(2^k)枚举掉有哪些边出现在MST上。但是,如何设置权值使得要求出现的边全数出现呢?

我们考虑裸的Kruskal算法。首先,先用冰茶姬将要求出现的边加入生成树(明显此时如果已经出现了环,则此边集本身即不合法,可以直接跳过该边集),然后将原图中的边按照权值排序后依次加入生成树。

假如添加一条原图中的边使得两个连通块联通,显然这条边应该出现在MST上;否则,该边便不应该出现在MST上。而这个的充要条件,便是现在的MST上,该边连接的两个节点间的路径中,所有边的权值都不大于当前试图加入的边的权值。于是就直接遍历路径上所有边并让其边权与当前边权取(min)即可。

明显该算法是(2^knm)的,就算使用树上倍增也是(2^kmlog n)的,根本不可能通过所有数据。考虑优化。

观察到(k)条边最多与(2k)个点有关,换言之无论它们有哪些边在MST中,它们所影响的只是少部分节点。这就引起了一个重要推论——在所有(2^k)个MST中,有大部分边是相同的。那么,具体是哪些边相同呢?

我们考虑先将(k)条边全数加入MST中(不管出不出现环),得到一张图。现在如果我们加入了更少的边,明显该图中的连通块数只会更多不会更少;故此时MST中还需添加的边集,一定是所有(2^k)种MST的公共边集。于是我们就可以预先把这些公共边集加入生成树(换言之,就是把原图缩点)。在缩完点后,明显此图最多剩余(k+1)个连通块(即,考虑一棵包含所有(k)条边的MST,然后再拆去此(k)条边,最多就剩余(k+1)个连通块)。

假如我们加入的新边是(k)条边的某个子集的话,明显它可能并不能使此(k+1)个连通块全数联通;故我们需要再加入一些原边。而此处的原边,一定是此(k+1)个连通块的MST上的边,即最多(k)条边。

于是我们只需首先把被钦定的子集中的边加入MST(如上文所说,记得判环),然后依次考虑该(k)条原图中的边并尝试加入MST即可。

此时,该算法便被优化到了(2^kk^2)。使用树上倍增,可以优化到(2^kklog k),但明显因为(k)过小,该优化没有任何意义。直接使用(O(2^kk^2))的算法即可通过。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,p,q,r,dsu[100100],col[100100];
ll num[100100],res,now;
void init(int len){for(int i=1;i<=len;i++)dsu[i]=i;}
int find(int x){return dsu[x]==x?x:dsu[x]=find(dsu[x]);}
bool merge(int x,int y){x=find(x),y=find(y);if(x==y)return false;dsu[y]=x;return true;}
struct edge{
	int x,y,z;
	edge(int X=0,int Y=0,int Z=0){x=X,y=Y,z=Z;}
	friend bool operator <(const edge &u,const edge &v){return u.z<v.z;}
}e[300100];
int u[300100],v[300100],w[300100];
bool on[300100];
vector<int>g[300100];
bool dfs1(int x,int fa,int dest,int lim){
	if(x==dest)return true;
	for(auto i:g[x]){
		int y=(u[i]^v[i]^x);
		if(y==fa)continue;
		if(dfs1(y,x,dest,lim)){w[i]=min(w[i],lim);return true;}
	}
	return false;
}
ll dfs2(int x,int fa){
	ll ret=num[x];
	for(auto i:g[x]){
		int y=(u[i]^v[i]^x);
		if(y==fa)continue;
		ll tmp=dfs2(y,x);
		if(i<p)now+=tmp*w[i];
		ret+=tmp;
	}
	return ret;
}
void func(int mask){
	init(r);
	for(int i=1;i<=r;i++)g[i].clear();
	for(int i=0;i<p;i++)if((mask&(1<<i))&&merge(u[i],v[i]))g[u[i]].push_back(i),g[v[i]].push_back(i),w[i]=0x3f3f3f3f;
	for(int i=p;i<q;i++)if(merge(u[i],v[i]))g[u[i]].push_back(i),g[v[i]].push_back(i);else dfs1(u[i],0,v[i],w[i]);
	now=0;
	dfs2(col[1],0);
	res=max(res,now);
}
int main(){
	scanf("%d%d%d",&n,&m,&p);
	for(int i=0,x,y,z;i<m;i++)scanf("%d%d%d",&x,&y,&z),e[i]=edge(x,y,z);
	sort(e,e+m);
	init(n);
	for(int i=0;i<p;i++)scanf("%d%d",&u[i],&v[i]),merge(u[i],v[i]);
	for(int i=0;i<m;i++)on[i]=merge(e[i].x,e[i].y);
	init(n);
	for(int i=0;i<m;i++)if(on[i])merge(e[i].x,e[i].y);
	for(int i=1;i<=n;i++)if(dsu[i]==i)col[i]=++r;
	for(int i=1,x;i<=n;i++)col[i]=col[find(i)],scanf("%d",&x),num[col[i]]+=x;
	q=p;
	for(int i=0;i<m;i++)if(merge(e[i].x,e[i].y))u[q]=col[e[i].x],v[q]=col[e[i].y],w[q]=e[i].z,q++;
	for(int i=0;i<p;i++)u[i]=col[u[i]],v[i]=col[v[i]];
	for(int i=1;i<(1<<p);i++)func(i);
	printf("%lld
",res);
	return 0;
}

原文地址:https://www.cnblogs.com/Troverld/p/14621781.html