【题解】Luogu P3639 [APIO2013] 道路费用

\(k\le 20\),考虑 \(O(2^k)\) 暴力枚举加入的边。但是边数很大,时间复杂度很高无法承受。

考虑在一开始强制选这 \(k\) 条边,然后跑最小生成树,此时加入的边就是一定会加入的边。设这个边集为 \(S\)

\(S\) 连接的连通块缩成点,点数为 \(O(k)\)。再在原图上对这些点跑最小生成树,设加入的边集为 \(T\),则 \(T\) 为加入那 \(k\) 条边后有可能在最小生成树中的边。数量也为 \(O(k)\)

然后暴力枚举强制加入的边,用 \(T\) 跑出最小生成树,再用 \(T\) 中的非树边限制强制加入的边即可。具体地,对于一条非树边 \((u,v)\),树上 \(u\)\(v\) 的路径中的所有边都应该大于等于这条非树边的边权。暴力跳父亲即可。

考虑统计答案,只需要计算通过每条边的人数,用 dfs 计算子树权值和即可。

时间复杂度 \(O(m\log m+2^kk^2)\)

#include<bits/stdc++.h>
#define int long long
#define il inline
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
#define pb push_back
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=3e5+5;
int n,m,k,a[maxn],bel[maxn];
bool vis[maxn];
vector<int> dk;
vector<pii> e[maxn];
struct edge{
	int u,v,w;
	il bool operator<(const edge &x)const{
		return w<x.w;
	}
}em[maxn],ek[maxn];
vector<edge> et;
int fa[maxn],sz[maxn],ans;
int mxe[maxn],dep[maxn],tof[maxn];
il void init(){
	for(int i=1;i<=n;i++){
		fa[i]=i,sz[i]=1;
	}
}
il int find(int x){
	return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
	u=find(u),v=find(v);
	if(u==v){
		return ;
	}
	if(sz[u]>sz[v]){
		sz[u]+=sz[v],fa[v]=u;
	}
	else{
		sz[v]+=sz[u],fa[u]=v;
	}
}
il void dfs(int u){
	dep[u]=dep[fa[u]]+1;
	sz[u]=a[u];
	for(pii i:e[u]){
		int v=i.fir,w=i.sec;
		if(v!=fa[u]){
			fa[v]=u;
			tof[v]=w;
			dfs(v);
			sz[u]+=sz[v];
		}
	}
}
il void solve(int S){
	for(int u:dk){
		fa[u]=u,sz[u]=1,e[u].clear();
	}
	for(int i=1,u,v;i<=k;i++){
		if(S>>(i-1)&1){
			u=bel[ek[i].u],v=bel[ek[i].v];
			if(find(u)==find(v)){
				return ;
			}
			merge(u,v);
			e[u].pb(mp(v,-i));
			e[v].pb(mp(u,-i));
		}
	}
	for(int i=0;i<et.size();i++){
		vis[i]=0;
	}
	for(int i=0,u,v,w;i<et.size();i++){
		u=et[i].u,v=et[i].v,w=et[i].w;
		if(find(u)!=find(v)){
			merge(u,v);
			vis[i]=1;
			e[u].pb(mp(v,w));
			e[v].pb(mp(u,w));
		}
	}
	for(int i=1;i<=k;i++){
		mxe[i]=INT_MAX;
	}
	for(int u:dk){
		dep[u]=tof[u]=fa[u]=sz[u]=0;
	}
	dfs(bel[1]);
//	puts("666");
	for(int i=0,u,v,w;i<et.size();i++){
//		cout<<i<<"\n";
		if(vis[i]){
			continue;
		}
		u=et[i].u,v=et[i].v,w=et[i].w;
		if(dep[u]<dep[v]){
			swap(u,v);
		}
//		cout<<dep[u]<<" "<<dep[v]<<"\n";
		while(dep[u]>dep[v]){
//			puts("666");
			if(tof[u]<0){
				mxe[-tof[u]]=min(mxe[-tof[u]],w);
			}
			u=fa[u];
		}
		while(u!=v){
			if(tof[u]<0){
				mxe[-tof[u]]=min(mxe[-tof[u]],w);
			}
			if(tof[v]<0){
				mxe[-tof[v]]=min(mxe[-tof[v]],w);
			}
			u=fa[u],v=fa[v];
		}
	}
	for(int u:dk){
		if(tof[u]<0){
			tof[u]=-mxe[-tof[u]];
		}
	}
	int res=0;
	for(int u:dk){
		if(tof[u]<0){
			res-=tof[u]*sz[u];
		}
	}
	ans=max(ans,res);
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
//	cout<<cplx::usdmem();
	ios::sync_with_stdio(0),cin.tie(0);
//	freopen("toll5.in","r",stdin);
	cin>>n>>m>>k;
//	cout<<n<<" "<<m<<" "<<k<<"\n";
	for(int i=1;i<=m;i++){
		cin>>em[i].u>>em[i].v>>em[i].w;
	}
	init(),sort(em+1,em+m+1);
	for(int i=1;i<=k;i++){
		cin>>ek[i].u>>ek[i].v;
		merge(ek[i].u,ek[i].v);
	}
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1,u,v;i<=m;i++){
		u=em[i].u,v=em[i].v;
		if(find(u)!=find(v)){
			merge(u,v);
			vis[i]=1;
		}
	}
	init();
	for(int i=1,u,v;i<=m;i++){
		if(vis[i]){
			u=find(em[i].u);
			v=find(em[i].v);
			if(sz[u]>sz[v]){
				sz[u]+=sz[v];
				a[u]+=a[v];
				fa[v]=u;
			}
			else{
				sz[v]+=sz[u];
				a[v]+=a[u];
				fa[u]=v;
			}
		}
	}
	for(int i=1;i<=n;i++){
		bel[i]=find(i);
		if(bel[i]==i){
			dk.pb(i);
		}
	}
	init();
	for(int i=1,u,v,w;i<=m;i++){
		u=bel[em[i].u],v=bel[em[i].v],w=em[i].w;
		if(find(u)!=find(v)){
			et.pb((edge){u,v,w});
			merge(u,v);
		}
	}
	for(int S=0;S<1<<k;S++){
//		cout<<bitset<15>(S)<<"\n";
		solve(S);
	}
	cout<<ans;
	return 0;
}
}
signed main(){return asbt::main();}
/*
100000 299989 12
*/
posted @ 2025-02-04 22:43  zhangxy__hp  阅读(27)  评论(0)    收藏  举报