【题解】#2019 [zr联赛集训day3]史上第四简洁的题面
一共只有一条神仙路,也就是说一旦我探出了一条路是神仙路,剩下的路就可以随便走了。
堵不堵?堵住哪条边?
该点到其最短路树上的父亲的边
\(dp[i]\)表示\(i\)号结点的答案,建出原图以\(v\)为根的最短路树,考虑对手要堵一定会堵该点到其最短路树上的父亲的边,
求出堵住了他到最短路树上的父亲的边之后到v的最短路,设其为\(val[i]\)
最优决策一定不会成环,所以之前得到的一些边没有堵住的信息不会影响当前点的决策,就是说不会同一条边走多次,所以之前走过的边现在的决策一定不会再走了,不会走出环,因为走环不如根本就不进入这个环【1】
为什么可以交换 \(\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;
}