P4374 [USACO18OPEN]Disruption---------------优美的暴力
https://www.luogu.org/problemnew/show/P4374
题目描述
Farmer John自豪于他所经营的交通发达的的农场。这个农场是由NN块牧场(2 \leq N \leq 50,0002≤N≤50,000)组成的,N-1N−1条双向道路将它们连接起来,每一条道路的都为一单位长度。Farmer John注意到,从任何一块牧场到另一块牧场,都能通过一组合适的道路到达。
尽管FJ的农场现在是连通的,他担心如果有一条道路被阻断会发生什么,因为这事实上会将他的农场分为两个不相交的牧场集合,奶牛们只能够在每一个集合内移动但不能在集合间移动。于是FJ又建造了MM条额外的双向道路(1 \leq M \leq 50,0001≤M≤50,000),每一条的长度都是一个至多为10^9109的正整数。奶牛们仍然可以使用原有的道路进行移动,除非其中的某些被阻断了。
如果某条原有的道路被阻断了,农场就会被分为两块不相交的区域,那么FJ就会从他的额外修建的道路中选择一条能够重建这两块区域的连通性的,取代原来那条,从而奶牛们又可以从任何一块牧场去往另一块牧场。
对于农场上每一条原有的道路,帮助FJ选出最短的替代用的道路。
输入输出格式
输入格式:
输入的第一行包含NN和MM。接下来的N-1N−1行,每行用整数pp和qq描述了一条原有的道路,其中p \neq qp≠q是这条道路连接的两块牧场(在1 \ldots N1…N范围内)。剩下的MM行,每行用三个整数pp、qq和rr描述了一条额外的道路,其中rr是这条道路的长度。任何两块牧场之间至多只有一条道路。
输出格式:
对原有的N-1N−1条道路的每一条,按照它们在输入中出现的顺序,输出如果这条道路被阻断的话,能够重新连接农场的最短的替代用道路的长度。如果不存在合适的替代用的道路,输出-1。
输入输出样例
7
7
8
5
5
解法一: ** 暴力-子树-dfs序-莫队。**
• 建好树后就会发现,对一条树边产生贡献的额外边连接了深度较低的节点的
子树上的点与外界的点。那么有暴力的思路了。
• dfs序自然是节点序,但主要通过节点找到额外边,建立联系。对于额外边将其标记于它
连接的两点上。 将每条边处理出来作为询问。那么,对于询问我们保存了当前边深度较
低的节点的子树在dfs序中连续的一段区间。答案就是这段区间中只出现了一个点的额外
边中长度最小的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,x,y,z,kf,ans[50050],tl=1,tr; 4 int head[50050],nex[100050],ver[100050],pos[50050],tot=1; 5 int hd[50050],ne[100050],ve[100050],sot; 6 int siz[50050],bel[50050],top,cnt; 7 bool vis[50050],intop[50050]; 8 int t[50050],TOP; 9 void calc_push(int u) 10 { 11 if(intop[u]) 12 return ; 13 t[++TOP]=u; 14 intop[u]=true; 15 u=TOP; 16 int v=u>>1; 17 while(v>=1) 18 { 19 if(t[u]>t[v]) 20 return ; 21 swap(t[u],t[v]); 22 u=v;v>>=1; 23 } 24 } 25 void calc_pop() 26 { 27 while(TOP&&!vis[t[1]]) 28 { 29 intop[t[1]]=false; 30 t[1]=t[TOP--]; 31 int u=1,v=2; 32 while(v<=TOP) 33 { 34 if(v<TOP&&t[v]>t[v+1]) 35 ++v; 36 if(t[v]>t[u]) 37 break ; 38 swap(t[v],t[u]); 39 u=v;v<<=1; 40 } 41 } 42 } 43 struct node 44 { 45 int l,r,pos; 46 bool operator<(const node & p) const 47 { 48 return bel[l]==bel[p.l]? (bel[l]&1)^(r<p.r):bel[l]<bel[p.l]; 49 } 50 }num[50050]; 51 struct nod 52 { 53 int a,b,v; 54 bool operator<(const nod & p)const 55 { 56 return v<p.v; 57 } 58 }path[50050]; 59 void ad(int x,int y) 60 { 61 ne[++sot]=hd[x]; 62 ve[sot]=y; 63 hd[x]=sot; 64 } 65 void add(int x,int y) 66 { 67 nex[++tot]=head[x]; 68 ver[tot]=y; 69 head[x]=tot; 70 } 71 void dfs(int u,int fa) 72 { 73 pos[u]=++cnt; 74 for(int i=head[u];i;i=nex[i]) 75 if(ver[i]!=fa) 76 { 77 dfs(ver[i],u); 78 siz[u]+=siz[ver[i]]+1; 79 num[++top].l=pos[ver[i]]; 80 num[top].r=pos[ver[i]]+siz[ver[i]]; 81 num[top].pos=i>>1; 82 } 83 } 84 void calc(int u) 85 { 86 for(int i=hd[u];i;i=ne[i]) 87 if(vis[ve[i]]^=1) 88 calc_push(ve[i]); 89 } 90 int main() 91 { 92 scanf("%d%d",&n,&m); 93 kf=sqrt(n);bel[n]=n/kf; 94 for(int i=1;i<n;++i) 95 { 96 bel[i]=i/kf; 97 scanf("%d%d",&x,&y); 98 add(x,y);add(y,x); 99 } 100 dfs(1,0); 101 sort(num+1,num+top+1); 102 for(int i=1;i<=m;++i) 103 scanf("%d%d%d",&path[i].a,&path[i].b,&path[i].v); 104 sort(path+1,path+m+1); 105 memset(ans,-1,sizeof(ans)); 106 for(int i=1;i<=m;++i) 107 { 108 ad(pos[path[i].a],i); 109 ad(pos[path[i].b],i); 110 } 111 for(int i=1;i<=top;++i) 112 { 113 while(tl<num[i].l) calc(tl++); 114 while(tl>num[i].l) calc(--tl); 115 while(tr<num[i].r) calc(++tr); 116 while(tr>num[i].r) calc(tr--); 117 calc_pop(); 118 if(TOP) 119 ans[num[i].pos]=path[t[1]].v; 120 } 121 for(int i=1;i<n;++i) 122 printf("%d\n",ans[i]); 123 return 0; 124 }
解法二: 并查集压缩路径
• 需要最短的路径,那么就如果一条路径上已经存在对于它来说最优的解了,那么这条路径也没有存在的必要了。
• 为了使每条路经尽可能的早的得到答案,我们可以对额外边预先排序,实际上也达到了整体的最优性。
• 额,由于处理路径并不方便,我们可以将每条边都归属于其下方的树上节点。从而转化为节点的压缩。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,x,y,z; 4 int head[50050],nex[100050],ver[100050],fa[50050],tot=1; 5 int f[50050][17],deep[50050],pos[50050],ans[50050]; 6 struct node 7 { 8 int a,b,v; 9 bool operator<(const node & p)const 10 { 11 return v<p.v; 12 } 13 }num[50050]; 14 int find(int u) 15 { 16 return fa[u]==u? u:fa[u]=find(fa[u]); 17 } 18 void add(int x,int y) 19 { 20 nex[++tot]=head[x]; 21 ver[tot]=y; 22 head[x]=tot; 23 } 24 void dfs(int u,int fa) 25 { 26 f[u][0]=fa;deep[u]=deep[fa]+1; 27 for(int i=1;i<=16;++i) 28 f[u][i]=f[f[u][i-1]][i-1]; 29 for(int i=head[u];i;i=nex[i]) 30 if(ver[i]!=fa) 31 { 32 dfs(ver[i],u); 33 pos[ver[i]]=i>>1; 34 } 35 } 36 int calc_lca(int x,int y) 37 { 38 if(deep[x]<deep[y]) 39 swap(x,y); 40 int tmp=deep[x]-deep[y]; 41 for(int i=0;i<=16;++i) 42 if(tmp&(1<<i)) 43 x=f[x][i]; 44 if(x==y) 45 return x; 46 for(int i=16;i>=0;--i) 47 if(f[x][i]!=f[y][i]) 48 { 49 x=f[x][i]; 50 y=f[y][i]; 51 } 52 return f[x][0]; 53 } 54 int main() 55 { 56 scanf("%d%d",&n,&m); 57 for(int i=1;i<=n;++i) 58 fa[i]=i; 59 for(int i=1;i<n;++i) 60 { 61 scanf("%d%d",&x,&y); 62 add(x,y);add(y,x); 63 } 64 dfs(1,0); 65 for(int i=1;i<=m;++i) 66 scanf("%d%d%d",&num[i].a,&num[i].b,&num[i].v); 67 sort(num+1,num+m+1);memset(ans,-1,sizeof(ans)); 68 for(int i=1;i<=m;++i) 69 { 70 x=num[i].a;y=num[i].b; 71 int ed=deep[calc_lca(x,y)]; 72 while(deep[(x=find(x))]>ed) 73 { 74 ans[pos[x]]=num[i].v; 75 fa[x]=f[x][0]; 76 x=find(fa[x]); 77 } 78 while(deep[(y=find(y))]>ed) 79 { 80 ans[pos[y]]=num[i].v; 81 fa[y]=f[y][0]; 82 y=find(fa[y]); 83 } 84 } 85 for(int i=1;i<n;++i) 86 printf("%d\n",ans[i]); 87 return 0; 88 }
解法三: 逆向倍增
• 倍增在处理树上问题有极大的优越性。传统的树上倍增往往用于快速搜寻路径,强大的二进制极大优化了它的效率。
• 实际上,我们完全可以不排序,将每条路经实时更新到路径上。
• 实现这种操作只需要一个普通的倍增数组。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,m,x,y,z; 4 int head[50050],nex[100050],ver[100050],fa[50050],tot=1; 5 int f[50050][17],s[50050][17],deep[50050],pos[50050],ans[50050]; 6 struct node 7 { 8 int a,b,v; 9 bool operator<(const node & p)const 10 { 11 return v>p.v; 12 } 13 }num[50050]; 14 int find(int u) 15 { 16 return fa[u]==u? u:fa[u]=find(fa[u]); 17 } 18 void add(int x,int y) 19 { 20 nex[++tot]=head[x]; 21 ver[tot]=y; 22 head[x]=tot; 23 } 24 void dfs(int u,int fa) 25 { 26 f[u][0]=fa;deep[u]=deep[fa]+1; 27 for(int i=1;i<=16;++i) 28 f[u][i]=f[f[u][i-1]][i-1]; 29 for(int i=head[u];i;i=nex[i]) 30 if(ver[i]!=fa) 31 { 32 dfs(ver[i],u); 33 pos[ver[i]]=i>>1; 34 } 35 } 36 int calc_lca(int x,int y) 37 { 38 if(deep[x]<deep[y]) 39 swap(x,y); 40 int tmp=deep[x]-deep[y]; 41 for(int i=0;i<=16;++i) 42 if(tmp&(1<<i)) 43 x=f[x][i]; 44 if(x==y) 45 return x; 46 for(int i=16;i>=0;--i) 47 if(f[x][i]!=f[y][i]) 48 { 49 x=f[x][i]; 50 y=f[y][i]; 51 } 52 return f[x][0]; 53 } 54 void calc(int u,int ce,int val) 55 { 56 for(int i=0;i<=16;++i) 57 if(ce&(1<<i)) 58 { 59 s[u][i]=val; 60 u=f[u][i]; 61 } 62 } 63 int main() 64 { 65 scanf("%d%d",&n,&m); 66 for(int i=1;i<=n;++i) 67 fa[i]=i; 68 for(int i=1;i<n;++i) 69 { 70 scanf("%d%d",&x,&y); 71 add(x,y);add(y,x); 72 } 73 dfs(1,0); 74 for(int i=1;i<=m;++i) 75 scanf("%d%d%d",&num[i].a,&num[i].b,&num[i].v); 76 sort(num+1,num+m+1);memset(ans,-1,sizeof(ans)); 77 memset(s,127,sizeof(s)); 78 for(int i=1;i<=m;++i) 79 { 80 x=num[i].a;y=num[i].b; 81 int ed=deep[calc_lca(x,y)]; 82 calc(x,deep[x]-ed,num[i].v); 83 calc(y,deep[y]-ed,num[i].v); 84 } 85 for(int k=16;k>=1;--k) 86 for(int i=1;i<=n;++i) 87 { 88 s[i][k-1]=min(s[i][k-1],s[i][k]); 89 s[f[i][k-1]][k-1]=min(s[f[i][k-1]][k-1],s[i][k]); 90 } 91 for(int i=2;i<=n;++i) 92 ans[pos[i]]=s[i][0]; 93 for(int i=1;i<n;++i) 94 printf("%d\n",ans[i]==s[0][0]? -1:ans[i]); 95 return 0; 96 }