[CF757F] Team Rocket Rises Again [最短路树][支配树]

[ L i n k \frak{Link} Link]


题意:给一 n \mathcal{n} n m \mathcal{m} m边无向图。给出点 S \mathcal{S} S。选一个点 u ≠ S \mathcal{u\ne S} u̸=S使删掉点 u \mathcal{u} u后,有尽可能多的点到 S \mathcal{S} S的最短距离改变。


考虑一下跑一个 S \mathcal{S} S作起点的最短路 D A G \mathcal{DAG} DAG
题目要求变成选一个子树大小最大的割点。 是吗?

这是 D A G \mathcal{DAG} DAG,而不是树。
删掉一个割点并不能让它“子树”内的每一个点都不跟 S \mathcal{S} S联通,比如说 s → u , u → v , v → w , u → w , v → o \mathcal{s\to u,u\to v,v\to w,u\to w,v\to o} su,uv,vw,uw,vo
这个时候删掉 v \mathcal{v} v会让 o \mathcal{o} o独立出来,所以 v \mathcal{v} v确实是割点,然而删掉它, S \mathcal{S} S还能到达 w \mathcal{w} w
更何况可能图里面根本就不存在割点。。(比如一个环)

所以这个时候我们要求的是有向无环图的支配树
这个不像有向图里面的那么麻烦, t o p o s o r t \mathcal{toposort} toposort一次,对一个点 v \mathcal{v} v,找到所有能够到达它的点 x \mathcal{x} x
求出这些点的 l c a : u \mathcal{lca:u} lca:u
然后就可以在 D o m i n a t o r    T r e e \mathcal{Dominator\;Tree} DominatorTree上面把 u → v \mathcal{u\to v} uv连一条边。
最后在 D o m i n a t o r    T r e e \mathcal{Dominator\;Tree} DominatorTree上面找根的儿子里面 s i z \mathcal{siz} siz最大那个就可以啦。
实际操作嫌麻烦的话求根以外所有点的也可以。(当然如果没有实际建支配树就一定得这么做了)
L C A \mathcal{LCA} LCA的话,不需要在求 D o m i n a t o r    T r e e \mathcal{Dominator\;Tree} DominatorTree之前预处理 D A G \mathcal{DAG} DAG p r e [ i ] [ x ] \mathcal{pre[i][x]} pre[i][x]。(如果倍增)
因为求 D o m i n a t o r    T r e e \mathcal{Dominator\;Tree} DominatorTree是按照拓扑序的。用到的都是已经求过的。


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
using namespace std;
#define add_edge(a,b,c) nxt[++tot]=head[a],head[a]=tot,to[tot]=b,val[tot]=c
#define add_up(a,b,c) upnxt[++uptot]=uphead[a],uphead[a]=uptot,upto[uptot]=b
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
char frBB[1<<12], *frS=frBB, *frT=frBB;
int read() {
	int x = 0;
	char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
	return x;
}
int head[200015];
int nxt[600015];
int to[600015];
int u[300015];
int v[300015];
int w[300015];
int val[600015];
long long dis[200015];
int pre[200015][25];
int uphead[200015];
int upnxt[300015];
int upto[300015];
int deg[200015];
int siz[200015];
int qwq[200015];
int dep[200015];
bool vis[200015];
int n, m, s, tot, uptot, loglog;
#define PII pair<long long, int>
priority_queue<PII, vector<PII>, greater<PII> > QvQ; 
void dijkstra() {
	for (register int i = 1; i <= n; ++i) dis[i] = 2147483647147483647ll;
	dis[s] = 0;
	QvQ.push(make_pair(0, s));
	int cnt = 0;
	while (!QvQ.empty()) {
		++cnt;
		int x = QvQ.top().second;
		QvQ.pop();
		vis[x] = 1;
		for (register int i = head[x]; i; i = nxt[i]) {
			if (dis[to[i]] <= dis[x] + val[i]) continue;
			dis[to[i]] = dis[x] + val[i];
			if (vis[to[i]]) continue;
			QvQ.push(make_pair(dis[to[i]], to[i]));
		}
	}
}
void Toposort() {
	qwq[++qwq[0]] = s;
	int qwqhead = 0;
	while (qwq[0] != qwqhead) {
		int x = qwq[++qwqhead];
		for (register int i = head[x]; i; i = nxt[i]) {
			--deg[to[i]];
			if (!deg[to[i]]) {
				qwq[++qwq[0]] = to[i];
			}
		}
	}
	n = qwq[0];
}
int Doubly(int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	for (register int i = loglog; i >= 0; --i) {
		if (dep[pre[x][i]] >= dep[y]) {
			x = pre[x][i];
		}
	}
	if (x == y) return x;
	for (register int i = loglog; i >= 0; --i) {
		if (pre[x][i] != pre[y][i]) {
			x = pre[x][i];
			y = pre[y][i];
		}
	}
	return pre[x][0];
}
void DominatorTree() {
	dep[s] = 1;
	for (register int i = 2; i <= n; ++i) {
		int t(0);
		int x(qwq[i]);
		for (int j = uphead[x]; j; j = upnxt[j]) {
			if (!t) t = upto[j];
			else t = Doubly(t, upto[j]);
		}
		pre[x][0] = t;
		dep[x] = dep[t] + 1;
		for (int j = 1; j <= loglog; ++j) {
			pre[x][j] = pre[pre[x][j-1]][j-1]; 
		}
	}
}
int main() {
	n = read();
	m = read();
	s = read();
 	loglog = (int) (log(1.0 * n) / log(2.0)) + 1;
	for (int i = 1; i <= m; ++i) {
		u[i] = read();
		v[i] = read();
		w[i] = read();
		add_edge(u[i], v[i], w[i]);
		add_edge(v[i], u[i], w[i]);
	}
	dijkstra();
	tot = 0;
	for (register int i = 1; i <= n; ++i) {
		head[i] = 0;
	}
	for (register int i = 1; i <= m; ++i) {
		if (dis[u[i]] > dis[v[i]]) swap(u[i], v[i]);
		if (dis[u[i]] + w[i] == dis[v[i]]) {
			add_edge(u[i], v[i], w[i]);
			++deg[v[i]];
			add_up(v[i], u[i], w[i]);
		}
	}
	Toposort();
	for (register int i = loglog; i >= 0; --i) pre[s][i] = s;
	DominatorTree();
	for (int i = n; i > 1; --i) {
		siz[pre[qwq[i]][0]] += ++siz[qwq[i]];
	}
	int ans = 0;
	for (register int i = 2; i <= n; ++i) {
		ans = max(ans, siz[qwq[i]]);
	}
	printf("%d", ans);
    return 0;
}

99行第一个j被我打成了i调了半天,自闭了
dijkstradis[to[i]]<=被我打成了dis[to[i]]< 自闭闭了
注意开long long

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值