【题解】#2019 [zr联赛集训day3]史上第四简洁的题面

一共只有一条神仙路,也就是说一旦我探出了一条路是神仙路,剩下的路就可以随便走了。

堵不堵?堵住哪条边?

该点到其最短路树上的父亲的边

\(dp[i]\)表示\(i\)号结点的答案,建出原图以\(v\)为根的最短路树,考虑对手要堵一定会堵该点到其最短路树上的父亲的边,

求出堵住了他到最短路树上的父亲的边之后到v的最短路,设其为\(val[i]\)

最优决策一定不会成环,所以之前得到的一些边没有堵住的信息不会影响当前点的决策,就是说不会同一条边走多次,所以之前走过的边现在的决策一定不会再走了,不会走出环,因为走环不如根本就不进入这个环【1】

\[dp[i]=max(val[i],min(dp[j]+e_{i,j})) \]

为什么可以交换 \(\rm max\)\(\rm min\)?

$ max(val[i],min(dp[j]+e{i,j})) = min(max(val[i],dp[j]+e{i,j})) $$【3】

前者:\(dp[j] + e{i,j}\)中只有最小值可能成为答案,这个最小值和\(val[i]\)中最大的那个作为答案

后者:大于\(val[i]的dp[j] + e{i,j}\)可能成为答案,大于\(val[i]\)的最小\(dp[j] + e{i,j}\) 是答案,当最小的\(dp[j] + e{i,j}\) 不大于\(val[i]\)时,\(val[i]\)是答案

【1】所以从一个点走到终点的路径是简单的

怎么求\(val[i]\)

一张图,断掉一条边,求两点间的最短距离。使用\(dijkstra\)复杂度为\(O((n+m)logm)\)

求出最短路径树,用\(dijkstra\)\(O((n+m)logm)\)

对于每一个点求出终点到他的断边最短路,\(O(n(n+m)logm)\)

\(dp[]\),注意到【3】式形式上很像松弛操作,所以我们用\(dijkstra\)来求\(dp[]\),O((n+m)logm)$

为什么\(val[x]\)只会走一条非树边?

首先由于删掉了最短路树上的一条边,所以只用树边不可能连通。所以走非树边首要任务是让x和终点连通,假如走了两条非树边,使得我x和终点连通了,要么成环,要么可以用树边替换

算了,不会证,那么感性理解一下,就是\(val[x]\)由一些树边和一条非树边得到,但是我们不能确定路径条数。这条路径可能是终点(根节点)先随便走到某个点【2】(不一定是\(x\)的祖先),然后连一条非树边到\(x\)或者\(x\)子树内,如果连到\(x\)的子树内,那么再沿着父亲边往上跳到\(x\)

【2】断开最短路上\(x\)和他父亲的边后将树分成两个连通块,这个点一定是\(x\)不属于的那一个连通块,非树边的另一个端点一定在\(x\)所在的连通块

100pts

考虑如何更快地求出断父边最短路

考虑一条非树边能够贡献到哪些点的路径?答案是这条非树边的两个端点在树上的路径上所有的除去\(lca\)的点。也就是说,我们现在不从如何找到\(dp[i]\)的那条非树边入手,而是从边的角度考虑。

一条非树边\((y,z,w)\)贡献到\(val[x]\)\(val[x]=dep[z]-dep[x]+dep[y]+w\)

给所有非树边按照\(dep[y]+dep[z]+w\)作为权值从小到大排序,那么\(val[x]\)一定由权值最小的那条非树边转移

树并查集

我对于树上每一个点都开一个并查集,由于我的操作是对书上一条路径除去其\(lca\)的所有点进行区间覆盖,为了避免重复操作,我将覆盖完成的点的并查集合并。同一个并查集的点在树上一定是一个连通块。每当我合并两个并查集的时候,连接二者的边一定有一个端点是其中一个并查集的根。在一个并查集内部,根节点是没有被覆盖过的,一旦我们合并两个并查集,就必然有一个根节点不再是根节点,同时他也就被覆盖了。

那么在求得\(val[]\)之后,再做一遍\(dijkstra[]\)求得\(dp[]\)即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rd(){
	int res = 0, fl = 1; char c = getchar();
    while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
    while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
    return res * fl;
}
ll rdll(){
	ll res = 0, fl = 1; char c = getchar();
    while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
    while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
    return res * fl;
}
const int maxn = 1000010, maxm = 2000010;
ll dp[maxn], f[maxn], dis[maxn], C;
int fst[maxn], en(1), A, B, T, n, m, fa[maxn], faed[maxn], rt[maxn], tot, ent, head[maxn], anc[maxn];
bool vis[maxn];
priority_queue<pair<ll, int> > q;
bitset<2000005> used;
struct Edge{
	ll w;
	int to, nxt;
}ed[maxm<<1],edt[maxn<<1];
struct EDGE{
	ll val;
	int x,y;
}edg[maxm<<1];
void add(int u, int v, ll w){
	ed[++en].to=v,ed[en].nxt=fst[u],fst[u]=en,ed[en].w=w;
}
void dij(){
	memset(vis, 0, sizeof(vis));
	memset(dis, 0x3f, sizeof(dis));
	dis[T]=0;
	q.push(make_pair(0, T));
	while(q.size()){
		int u=q.top().second; q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int e(fst[u]), v(ed[e].to); e; e=ed[e].nxt, v=ed[e].to){
			if(dis[v] > dis[u] + ed[e].w){
				dis[v] = dis[u] + ed[e].w;
				used[faed[v] >> 1]=0;
				fa[v] = u; faed[v] = e;
				used[e >> 1]=1;
				q.push(make_pair(-dis[v], v));
			}
		}
	}
}
void dij2(){
	memset(vis, 0, sizeof(vis));
	memset(dp, 0x3f, sizeof(dp));
	dp[T]=0;
	q.push(make_pair(-dp[T], T));
	while(q.size()){
		int u=q.top().second;
		q.pop();
		for(int e(fst[u]), v(ed[e].to); e; e = ed[e].nxt, v = ed[e].to){
			if(dp[v] > max(f[v], dp[u] + ed[e].w)){
				dp[v] = max(f[v], dp[u] + ed[e].w);
				q.push(make_pair(-dp[v], v));
			}
		}
	}
}
bool cmp(EDGE e1, EDGE e2){
	return e1.val < e2.val;
}
int find(int x){
	return anc[x]==x?x:anc[x]=find(anc[x]);
}
int main(){
	n=rd(), m=rd(), T=rd();
	for(int i(1); i<=m; ++i){
		A=rd(), B=rd(), C=rdll();
		add(A, B, C), add(B, A, C); 
	}
	dij();
	for(int i(1);i<=n;++i) anc[i]=i;
	for(int i(2);i<=en;i+=2){
		if(!used[i >> 1])
			edg[++tot].x=ed[i].to, edg[tot].y=ed[i^1].to, edg[tot].val=dis[edg[tot].x]+dis[edg[tot].y]+ed[i].w;
	} 
	sort(edg+1,edg+tot+1,cmp);
	memset(f,0x3f,sizeof(f));
	for(int i(1);i<=tot;++i) {
		int x(edg[i].x),y(edg[i].y);
		if(!vis[x]||!vis[y]) continue;
		x=find(x), y=find(y);
		while(x ^ y){
			if(dis[x] < dis[y]) swap(x, y);
			f[x]=edg[i].val;
			x = anc[x] = find(fa[x]), y=find(y);
		}
	}
	f[T] = 0;
	for(int i(1);i<=n;++i) f[i]-=dis[i];
	dij2();
	for(int i(1);i<=n;++i) if(dp[i]>=0x3f3f3f3f3f3f3f) dp[i]=-1;
	for(int i(1);i<=n;++i) printf("%lld ", dp[i]); printf("\n");
	return 0;
}
posted @ 2021-10-16 18:14  _Buffett  阅读(74)  评论(0编辑  收藏  举报