APIO 2013 T2 Toll

想要解决这道题的最大难点是:如果我们前面插入了边的话,可能会改变树的形态,从而对后面的点的安排产生影响。每次都需要重构树形态。

考虑消除顺序的影响,枚举最终加入生成树的边。我们的要求是,每条生成树的边都处于最优连接状态,首先 \(2^k\) 枚举加入的所有边,然后的要求是,如果加入了从 \(x\)\(y\) 的边权为 \(w\) 的边,那么在原先的图的最小生成树中,\(x\)\(y\) 的路径,以及跨过 \(x\)\(y\) 的路径的已经加入的边都应当不小于它的边权。

我们找到最终的生成树需要的形态。

我们把需要加入的所有边全部边权赋为 \(0\) 加入最小生成树,然后得到一个我们需要的生成树的形态。为什么这样是对的呢?我们发现,如果我们把边的权值赋成 \(x\)\(y(x<y)\),那么只要它在最小生成树中,其连接的两个连通块是唯一的,因为其他的应该加入的边的连接方式不会因为当前边的权值而改变。

则只要权值达到可以加入最小生成树的要求,最终的生成树形态和权值无关。

则我们找到了一个暴力做法,枚举要加入的边,将其边权全部赋为 \(0\),生成一个 \(\text{MST}\)。然后在得到的有根树上,对于没有加入的所有边,其路径上的所有新边的权值都应当小于它,求出每条边边权最大值。因为原图是连通的,所以最大值一定存在。

这样的复杂度是 \(O(2^knm)\),考虑优化。

我们发现,如果我们一开始就把所有的边都加入并生成最小生成树,那么如果我们不再使用某些边,就是把这些边删掉之后,继续加入剩下的边。也就是我们用这些新边把原图划分成 \(k+1\) 个连通块,不使用某些新边,就是把它去掉,然后加入剩下的横跨两个块的边合并连通块。

于是我们可以考虑一个做法,我们记录一个表,\(mn_{i,j}\) 表示从 \(i\) \(j\) 的最小边权。我们每次枚举哪些块和自己的父亲断开(即不使用这些新边)用 \(Brouvka\)\(O(k\log k)\) 的重新把所有被分出去的连通块重新合成最小生成树。同时维护新的块的 \(mn\) 表。

然后我们遍历所有的 \(k^2\) 条边,在新生成的最小生成树上更新路径上的边权,最后计算答案。

因为我们每次更新边权是 \(O(k)\) 的,这样做的复杂度是 \(O(2^kk^3+m(\log n+\log m))\)。我们考虑优化复杂度。

发现我们其实是给整张满图进行更新,那么我们就可以统一更新。原理是,对于当前所有端点为 \(x\) 的边,我们枚举另一个端点和 \(x\)\(lca\),然后跑一个拓扑排序,从所有的叶子开始向上跳,累计下方的贡献和上方的贡献,一路更新,直到到达 \(x\) 的父亲,将最小值累计在这个点上。然后再从根节点往 \(x\) 扫描,一路累计累计上的最小值,从而 \(O(k)\) 的完成所有以 \(x\) 为端点的边的更新。

这样,我们就做到了 \(O(2^kk^2+m(\log n+\log m))\)。但是这样的实现非常麻烦,而且 \(O(2^kk^2)\) 的常数是满的(每次都跑完了整张图),很难过去。

那么,我们就需要进行改动。

我们发现一件事情:如果有边能对新边产生贡献,那么在去掉所有新边之后它一定是最小生成树上的边。

这一点是显然的,如果一条边的贡献没有被另一条边覆盖,它就应该优先于另一条边加入最小生成树。

所以,我们只需要记录小块意义下的无新边最小生成树的所有边。然后每次把要加入的新边加进去跑最小生成树。这其中会替代一些边,就只用这些边更新即可。每次要更新的边就被我们缩到了小于 \(k\) 条。

虽然没有了满图的性质,需要 \(O(k)\) 贡献,复杂度依然是 \(O(2^kk^2)\),但是常数远远跑不满,因为每次只要更新树上的两条路径。而且不会每次都是一条很长的链(因为那样就会存在替代关系),因此常数很小。

这样,我们只要做最小生成树,更新,然后对于 加入的每条边,用 \(maxvalue\)\(size\) 相乘做贡献即可。

struct edge{
	int x,y,c,t;
	edge(int _x,int _y,int _c,int _t){
		x=_x,y=_y,c=_c,t=_t;
	}
	bool operator <(const edge b)const{
		if(c==b.c)return b.x<x;
		return c<b.c;
	}
};
vt<edge>vall,vv[200005];
int fa[200005],n,m,k,a,b,c,xx[25],yy[25],p[200005],cnt=0,stk[200005],top;
ll mn[25][25],lstval[200005],totsz[25],sz[25];
vt<edge>vnew;
namespace MST{
	vt<edge>vall;
	int fa[200005],size;
	inline int head(int x){
		return fa[x]==x?x:fa[x]=head(fa[x]);
	}
	inline vt<edge> MST(){
		rep(i,0,size)fa[i]=i;
		vt<edge>res;
		sort(vall.begin(),vall.end());
		for(auto e:vall){
			if(head(e.x)!=head(e.y)){
				res.pb(e);
				fa[head(e.x)]=head(e.y);
			}
		}
		return res;
	}
};
inline void getblock(int x,int p){
	stk[++top]=x;
	for(auto j:vv[x])if(j.y!=p){
		lstval[j.y]=j.c;
		getblock(j.y,x);
	}
	if(!lstval[x]){
		cnt++;
		while(stk[top]!=x)fa[stk[top--]]=cnt;
		fa[stk[top--]]=cnt;
	}
}
vt<edge>T[25];
int pa[25],inq[25],tg[25],mxval[25];
inline void construct_tree(int x,int p){
	pa[x]=p;totsz[x]=sz[x];
	for(auto j:T[x])if(j.y!=p){
		construct_tree(j.y,x);
		totsz[x]+=totsz[j.y];
	}
}
inline ll getans(int msk){
	vt<edge>vs=vnew;ll ans=0;
	rd(i,k)if(msk>>i&1)vs.pb({fa[xx[i]],fa[yy[i]],0,1});
	MST::size=cnt,MST::vall=vs;
	vs=MST::MST();
	rp(i,cnt)T[i].clear(),mxval[i]=1e9;
	for(auto i:vs){
		T[i.x].pb({i.x,i.y,i.c,i.t}),T[i.y].pb({i.y,i.x,i.c,i.t});
	}
	construct_tree(cnt,-1);
	for(auto i:vnew)if(i.x!=i.y){
		int lstj=i.y;
		for(int j=i.x;j!=-1;j=pa[j])inq[j]=1;
		for(int j=i.y;!inq[j];j=pa[j],lstj=j)mxval[j]=min(mxval[j],i.c);
		for(int j=i.x;j!=lstj;j=pa[j]){
			mxval[j]=min(mxval[j],i.c);
		}
		for(int j=i.x;j!=-1;j=pa[j])inq[j]=0;
	}rd(i,k)if(msk>>i&1){
		if(pa[fa[xx[i]]]==fa[yy[i]])swap(xx[i],yy[i]);
		if(pa[fa[yy[i]]]!=fa[xx[i]])return 0;
		ans+=1ll*mxval[fa[yy[i]]]*totsz[fa[yy[i]]];
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>k;
	vt<edge>vall;
	rp(i,m){
		cin>>a>>b>>c;
		vall.pb({a,b,c,0});
	}
	rd(i,k){
		cin>>xx[i]>>yy[i];
		vall.pb({xx[i],yy[i],0,1}); 
	}MST::vall=vall;
	rp(i,n)cin>>p[i];
	MST::size=n;
	vt<edge>vans=MST::MST();
	for(auto j:vans){
		vv[j.x].pb({j.x,j.y,j.c,j.t});
		vv[j.y].pb({j.y,j.x,j.c,j.t});
	}
	getblock(1,0);
	for(auto j:vall){
		if(!j.t&&fa[j.x]!=fa[j.y]){
			vnew.pb({fa[j.x],fa[j.y],j.c,0});
		}
	}
	MST::size=cnt;
	MST::vall=vnew;
	vnew=MST::MST();
	rp(i,n)sz[fa[i]]+=p[i];
	ll ans=0;
	rd(i,1<<k)ans=max(ans,getans(i));
	cout<<ans<<endl;
	return 0;
}
//Crayan_r

另外,洛谷的本题数据似乎存在问题,存在权值相同的边,这种情况下原图最小生成树不唯一,实际上是不好做的。但是数据中权值相同的边不多只有两条,要么先 \(a\)\(b\) 要么先 \(b\)\(a\),那么就在权值相同的时候引入第二关键字,两种都试一下,总有一种能过的(是数据先动手的!!!)

posted @ 2023-03-06 11:35  jucason_xu  阅读(23)  评论(0编辑  收藏  举报