• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
The blog of EzSun
lyy && ?
博客园    首页    新随笔    联系   管理    订阅  订阅

小众宝藏图论问题总结

P3639 [APIO2013] 道路费用

太 tm 宝藏了。
看到 \(k\) 的数据范围,很难不让人想到 \(2^k\) 枚举边集。
因为当枚举的边集确定后,其最大价值是很好算的,只需用每一条非树边去约束即可。
但直接做肯定是不行的,因为 \(k\) 去到了 \(20\)。
不难发现每次都跑一遍最小生成树重复计算的边很多,考虑将原图简化。
发现存在一些旧边每次都会被加入,可以将这些边处理出来。处理的过程也很简单,将所有新边加入,再跑一遍最小生成树,此时被加入的旧边就是一定会被加入的边。
将这些边加入到图中并缩点,便剩下了最多 \(k+1\) 点,此时就可以再去 \(2^k\) 枚举子集做。
代码巨 tm 恶心

Code:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 7, M = 3e5 + 7, K = 27;
int n, m, k;
ll a[N], res[N];
int tt, nn;
int belong[N];
int vis[N];
ll minn[K];
struct edge_union{
	int from, to;
	ll w;
}ee[M], ne[K], pe[K];
struct uds{
	int fa[N];
	void init(int len){
		for(int i = 1; i <= len; i++) fa[i] = i;
	}
	int get(int x){
		if(fa[x] == x) return x;
		return fa[x] = get(fa[x]);
	}
	void merge(int x, int y){
		x = get(x), y = get(y);
		fa[x] = y;
	}
}A, B;
struct Edge{
	struct edge{
		int to;
		int pre;
	}e[K << 1];
	int head[K], tot;
	int fa[K], dep[K];
	ll sz[K];
	void init(){
		memset(head, 0, sizeof(head));
		memset(sz, 0, sizeof(sz));
		memset(dep, 0, sizeof(dep));
		memset(fa, 0, sizeof(fa));
		tot = 0;
	}
	void add(int x, int y){
		e[++tot] = {y, head[x]};
		head[x] = tot;
	}
	void dfs(int u, int father){
		fa[u] = father;
		sz[u] = res[u];
		dep[u] = dep[father] + 1;
		for(int i = head[u]; i; i = e[i].pre){
			int v = e[i].to;
			if(v == father) continue;
			dfs(v, u);
			sz[u] += sz[v];
		}
	}
	void update(int u, int v, ll w){
		if(dep[u] < dep[v]) swap(u, v);
		while(dep[u] > dep[v]){
			minn[u] = min(minn[u], w);
			u = fa[u];
		}
		while(u != v){
			minn[u] = min(minn[u], w);
			minn[v] = min(minn[v], w);
			u = fa[u];
			v = fa[v];
		}
	}
}E;
bool cmp1(edge_union a, edge_union b){
	return a.w < b.w;
}
void build(){
	A.init(N - 7);
	B.init(N - 7);
	sort(ee + 1, ee + m + 1, cmp1);
	for(int i = 1; i <= k; i++){
		int u = ne[i].from, v = ne[i].to;
		A.merge(u, v);
	}
	for(int i = 1; i <= m; i++){
		int u = ee[i].from, v = ee[i].to;
		if(A.get(u) == A.get(v)) continue;
		A.merge(u, v);
		B.merge(u, v);
	}
	for(int i = 1; i <= n; i++){
		if(!vis[B.get(i)]){
			belong[i] = vis[B.get(i)] = ++nn;
		}
		else belong[i] = vis[B.get(i)];
		res[belong[i]] += a[i];
	}
	for(int i = 1; i <= k; i++){
		ne[i] = {belong[ne[i].from], belong[ne[i].to], 0};
	}
	for(int i = 1; i <= m; i++){
		int u = ee[i].from, v = ee[i].to;
		ll w = ee[i].w;
		if(B.get(u) == B.get(v)) continue;
		pe[++tt] = {belong[u], belong[v], w};
		B.merge(u, v);
	}
	sort(pe + 1, pe + tt + 1, cmp1);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m >> k;
	for(int i = 1; i <= m; i++){
		cin >> ee[i].from >> ee[i].to >> ee[i].w;
	}
	for(int i = 1; i <= k; i++){
		cin >> ne[i].from >> ne[i].to;
	}
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	}
	build();
	ll ans = 0;
	for(int S = 0; S < (1 << k); S++){
		A.init(K);
		E.init();
		memset(minn, 0x3f, sizeof(minn));
		bool flg = 0;
		for(int i = 1; i <= k; i++){
			if((~S) & (1 << (i - 1))) continue;
			int u = ne[i].from, v = ne[i].to;
			if(A.get(u) == A.get(v)){
				flg = 1;
				break;
			}
			A.merge(u, v);
			E.add(u, v);
			E.add(v, u);
		}
		if(flg) continue;
		for(int i = 1; i <= tt; i++){
			int u = pe[i].from, v = pe[i].to;
			if(A.get(u) == A.get(v)) continue;
			A.merge(u, v);
			E.add(u, v);
			E.add(v, u);
		}
		E.dfs(belong[1], 0);
		for(int i = 1; i <= tt; i++){
			int u = pe[i].from, v = pe[i].to;
			ll w = pe[i].w;
			E.update(u, v, w);
		}
		ll sum = 0;
		for(int i = 1; i <= k; i++){
			if((~S) & (1 << (i - 1))) continue;
			int u = ne[i].from, v = ne[i].to;
			if(E.dep[u] < E.dep[v]) swap(u, v);
			sum += E.sz[u] * minn[u];
		}
		ans = max(ans, sum);
	}
	printf("%lld\n", ans);
	return 0;
}
posted @ 2026-05-18 21:16  EzSun599  阅读(4)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3