P4374 [USACO18OPEN]Disruption---------------优美的暴力

https://www.luogu.org/problemnew/show/P4374

题目描述

Farmer John自豪于他所经营的交通发达的的农场。这个农场是由NN块牧场(2 \leq N \leq 50,0002N50,000)组成的,N-1N1条双向道路将它们连接起来,每一条道路的都为一单位长度。Farmer John注意到,从任何一块牧场到另一块牧场,都能通过一组合适的道路到达。

尽管FJ的农场现在是连通的,他担心如果有一条道路被阻断会发生什么,因为这事实上会将他的农场分为两个不相交的牧场集合,奶牛们只能够在每一个集合内移动但不能在集合间移动。于是FJ又建造了MM条额外的双向道路(1 \leq M \leq 50,0001M50,000),每一条的长度都是一个至多为10^9109的正整数。奶牛们仍然可以使用原有的道路进行移动,除非其中的某些被阻断了。

如果某条原有的道路被阻断了,农场就会被分为两块不相交的区域,那么FJ就会从他的额外修建的道路中选择一条能够重建这两块区域的连通性的,取代原来那条,从而奶牛们又可以从任何一块牧场去往另一块牧场。

对于农场上每一条原有的道路,帮助FJ选出最短的替代用的道路。

输入输出格式

输入格式:

 

输入的第一行包含NN和MM。接下来的N-1N1行,每行用整数pp和qq描述了一条原有的道路,其中p \neq qpq是这条道路连接的两块牧场(在1 \ldots N1N范围内)。剩下的MM行,每行用三个整数pp、qq和rr描述了一条额外的道路,其中rr是这条道路的长度。任何两块牧场之间至多只有一条道路。

 

输出格式:

 

对原有的N-1N1条道路的每一条,按照它们在输入中出现的顺序,输出如果这条道路被阻断的话,能够重新连接农场的最短的替代用道路的长度。如果不存在合适的替代用的道路,输出-1。

 

输入输出样例

输入样例#1:
6 3
1 2
1 3
4 1
4 5
6 5
2 3 7
3 6 8
6 4 5
输出样例#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 }
代码

 

posted @ 2018-10-16 18:11  Hevix  阅读(246)  评论(0编辑  收藏  举报