Codecraft-17 and Codeforces Round #391 F - Team Rocket Rises Again (支配树)

题意:给一个带权无向图和源点s,问删掉一个点,最多能使多少个点的最短路变大(从s出发)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>

using namespace std;

#define N (200050)
#define M (1000010)
#define inf 0x3f3f3f3f
#define eps 1e-4
#define LL long long
#define pii pair<int, int>
#define MP make_pair
#define ls (i<<1)
#define rs (i<<1|1)
#define md (ll+rr>>1)
#define lson ll, md, ls
#define rson md+1, rr, rs
#define mod 1000000007


int fst[N], vv[M], nxt[M], cost[M], e;
LL dis[N];
int ans[N];
void init() {
	memset(fst, -1, sizeof fst);
	e = 0;
}

void add(int u,int v, int w) {
	vv[e] = v, cost[e] = w, nxt[e] = fst[u], fst[u] = e++;
}

vector<int> g[N], rg[N], bucket[N], T[N];
int sdom[N], par[N], dom[N], fa[N], lab[N];
int tid[N], rev[N], dfn;

void dfs(int u) {
	tid[u] = ++dfn;
	rev[dfn] = u;
	lab[dfn] = dfn;
	sdom[dfn] = dfn;
	fa[dfn] = dfn;
	for(int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if(!tid[v]) {
			dfs(v);
			par[tid[v]] = tid[u];
		}
		rg[tid[v]].push_back(tid[u]);
	}
}

int find(int u, int x = 0) {
	if(u == fa[u]) return x ? -1 : u;
	int v = find(fa[u], x+1);
	if(v < 0) return u;
	if(sdom[lab[fa[u]]] < sdom[lab[u]])
		lab[u] = lab[fa[u]];
	fa[u] = v;
	return x ? v : lab[u];
}

void Union(int u,int v) {
	fa[v] = u;
}

void Dom(int n, int s) {
	dfs(s);
	for(int i = n; i >= 1; --i) {
		for(int j = 0; j < rg[i].size(); ++j) {
			int v = rg[i][j];
			sdom[i] = min(sdom[i], sdom[find(v)]);
		}
		if(i > 1) bucket[sdom[i]].push_back(i);
		for(int j = 0; j < bucket[i].size(); ++j) {
			int w = bucket[i][j], v = find(w);
			if(sdom[v] == sdom[w]) dom[w] = sdom[w];
			else dom[w] = v;
		}
		if(i > 1) Union(par[i], i);
	}
	for(int i = 2; i <= n; ++i) {
		if(dom[i] != sdom[i]) dom[i] = dom[dom[i]];
		T[rev[i]].push_back(rev[dom[i]]);
		T[rev[dom[i]]].push_back(rev[i]);
	}
}


void spfa(int s) {
	queue<int> que;
	static bool vis[N];
	memset(vis, 0, sizeof vis);
	for(int i = 0; i < N; ++i) dis[i] = 1e16;
	dis[s] = 0;
	vis[s] = 1;
	que.push(s);
	while(!que.empty()) {
		int u = que.front();
		que.pop();
		vis[u] = 0;
		for(int i = fst[u]; ~i; i = nxt[i]) {
			int v = vv[i];
			if(dis[v] > dis[u] + cost[i]) {
				dis[v] = dis[u] + cost[i];
				if(!vis[v]) que.push(v), vis[v] = 1;
			}
		}
	}
	memset(vis, 0, sizeof vis);
	vis[s] = 1;
	que.push(s);
	while(!que.empty()) {
		int u = que.front();
		que.pop();
		for(int i = fst[u]; ~i; i = nxt[i]) {
			int v = vv[i];
			if(dis[v] == dis[u] + cost[i]) {
				g[u].push_back(v);
				if(!vis[v]) vis[v] = 1, que.push(v);
			}
		}
	}

}

void gao(int u, int p) {
	ans[u] = 1;
	for(int i = 0; i < T[u].size(); ++i) {
		int v = T[u][i];
		if(v == p) continue;
		gao(v, u);
		ans[u] += ans[v];
	}
}
int main() {
	int n, m, s;
	scanf("%d%d%d", &n, &m, &s);
	init();
	for(int i = 0; i < m; ++i) {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		add(u, v, w);
		add(v, u, w);
	}
	spfa(s);
	Dom(n, s);
	gao(s, -1);
	int mx = 0;
	for(int i = 1; i <= n; ++i) if(i != s)
		mx = max(ans[i], mx);
	printf("%d\n", mx);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值