uvalive 6206 Reduce the Maintenance Cost

题意:在10000个点的一个无向图中,定义P = 删除某条边之后有多少点对因此不连通,那么给每一条边定义一个val值,val = P*len,len 就是本身这条边自己的长度,每一条边的val就是维护费用要求连接这条边的两个点中的一个点来承担,现在每个城市最初有一个 初始的维护费用,问 怎么样的一个分配方案能取到 每个城市的维护费用最大值最小。

解法:

    很明显桥边具有val值,其他边是没有的,直接先进行一次双联通,形成一棵树,进行一次树形dp,统计子树的节点个数,这样就能统计出每一条桥边的val值,之后就是要统计最大的最小值是多少, 用二分来解,可以想到因为都是桥边构成的,所以必定没有换,那么就先对度为1 的边进行处理,如果能放到边上,就优先放在边上,这样类似一个拓扑序,一遍处理完所有的边,这样就可以判断出这个值是不是合法的。


#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <memory.h>
#include <vector>
#include <stack>
#include <queue>

using namespace std;

#define maxn 10010

typedef pair<int,int> PII;
typedef long long LL;
vector<PII> G[maxn] ,suo[maxn];

int n ,m;
LL cost[maxn];

void read() {
	for(int i = 1;i <= n;i++)
		scanf("%lld",cost + i);
	int u ,v, w;
	for(int i = 0 ;i < m; i++) {
		scanf("%d%d%d", &u, &v, &w);
		G[u].push_back( make_pair(v ,w));
		G[v].push_back( make_pair(u ,w));
	}
}

int num[maxn], kuainum[maxn];
int belong[maxn];
int ji ;
PII road[2 * maxn];
LL edgecost[2 * maxn];

struct BCC{
	int dfs_clock, bcc_cnt;
	int dfn[maxn], low[maxn], bccno[maxn];
	stack<int> S;
	void init() {
		dfs_clock = bcc_cnt = 0;
		for(int i = 1;i <= n;i++)
			G[i].clear();
		memset( dfn, 0, sizeof(dfn));
		while(! S.empty()) 
			S.pop();
	}

	int kuai ;
	void dfs(int u,int fa) {
		int size = G[u].size() ,x,flag = 1;
		S.push(u);

		dfn[u] = low[u] = ++ dfs_clock;
		for(int i = 0 ;i < size; i++) {
			int v = G[u][i].first;
			if(v == fa && 1 == flag) {
				flag = 0;
				continue;
			}
			if(0 == dfn[v]) {
				dfs(v,u);
				low[u] = min(low[u], low[v]);
			}
			else
				low[u] = min(low[u] ,dfn[v]);
		}
		if(low[u] == dfn[u]) {
			bcc_cnt ++;
			num[bcc_cnt] = 0;
			belong[bcc_cnt] = kuai;
			do{
				x = S.top();
				S.pop();
				bccno[x] = bcc_cnt;
				num[bcc_cnt] ++;
				kuainum[kuai] ++;
			}while(x != u);
		}
	}

	void find_bcc() {
		kuai = 0;
		for(int i = 1;i <= n;i++)
			if(0 == dfn[i]) {
				kuai ++;
				kuainum[kuai] = 0;
				dfs(i, -1);
			}
	}

	void build() {
		ji = 0;
		for(int i = 1;i <= n;i++) {
			int size = G[i].size();
			for(int f = 0 ;f < size;f++) {
				int x = i;
				int y = G[i][f].first;
				if(x > y)
					continue;
				int len = G[i][f].second;
				int u = bccno[x];
				int v = bccno[y];
				if(u == v)
					continue;
				suo[u].push_back( make_pair(v,ji));
				suo[v].push_back( make_pair(u,ji));
				road[ji] = make_pair(x, y);
				edgecost[ji] = len;
				ji ++;
			}
		}
	}
}townboy;

int sum ;
bool vis[maxn];

int dfs(int rt ) {
	vis[rt] = true;
	int size = suo[rt].size();
	int ret = num[rt];
	for(int i = 0 ;i < size;i ++) {
		int v = suo[rt][i].first;
		if(true == vis[v])
			continue;
		int id = suo[rt][i].second;
		LL tmp = dfs(v);
		edgecost[id] *= (LL) tmp * (kuainum[belong[v]] - tmp);
		ret += tmp;
	}
	return ret;
}

void debug() {
	for(int i = 0;i < ji; i++) {
		cout << edgecost[i] << endl;
	}
	cout << "debug end " << endl;
}

#define INF 0x3f3f3f3f3f3f3f3fLL

vector<int> E[maxn];
LL now[maxn];
int du[maxn];

bool test(LL lim) {
	queue<int> q;
	for(int i = 0;i < ji;i++)
		vis[i] = false;
	for(int i = 1;i <= n;i++) {
		now[i] = cost[i];
		E[i].clear();
		du[i] = 0;
	}
	for(int i = 0;i < ji;i++) {
		int u = road[i].first ,v = road[i].second;
		du[u] ++, du[v] ++;
		E[u].push_back(i);
		E[v].push_back(i);
	}
	for(int i = 1;i <= n;i++)
		if(1 == du[i])
			q.push(i);

	while(!q.empty()) {
		int u = q.front();
		q.pop();
		if(0 == du[u])
			continue;
		int size = E[u].size();
		int fun;
		for(int i = 0;i < size; i++) {
			if(false == vis[E[u][i]]) {
				fun = E[u][i];
				break;
			}
		}
		vis[fun] = true;
		int v = (u ^ road[fun].first ^ road[fun].second);
		du[u] --;
		du[v] --;
		if(now[u] + edgecost[fun] <= lim)
			now[u] += edgecost[fun];
		else if(now[v] + edgecost[fun] <= lim)
			now[v] += edgecost[fun];
		else
			return false;
		if(1 == du[v])
			q.push(v);
	}
	return true;
}

LL solve() {
	townboy.init();
	read();
	townboy.find_bcc();

	sum = townboy.bcc_cnt;
	for(int i = 1;i <= sum; i++)
		suo[i].clear();
	townboy.build();
	memset(vis, false, sizeof(vis));
	for(int i = 1;i <= sum; i++)
		if(false == vis[i])
			dfs(i);
	LL l = -1 ,r = INF, mid; 
	for(int i = 1;i <= n;i++)
		l = max(l, (LL)cost[i]);
	l --;
	while(l != r - 1) {
		mid = (l + r ) / 2;
		if(test(mid) == true)
			r = mid;
		else
			l = mid;
	}
	return r;
}

int main() {
	int cas;
	freopen("D:\\in.txt","r",stdin);
	freopen("D:\\a.txt","w",stdout);
	cin >> cas;
	for(int i = 0 ;i < cas; i++) {
		cin >> n >> m;
		printf("Case %d: ",i + 1);
		cout << solve() << endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值