[hdu6990]Directed Minimum Spanning Tree

模板题:在有向图中,对每一个点求以其为根的最小(外向)生成树

(当图是强连通时)可以使用朱刘算法,算法过程如下:

1.对每一个节点,选择指向该点的边权最小的边,即得到一张子图

2.任选这张子图的一个简单环,并对这个环执行以下操作——

(1)对于环上的边$(u,v)$?,将所有以$v$?为终点的边边权减去$(u,v)$??的边权

(2)新建一个点为将环上所有点合并后的结果,并删除新点的自环

3.重复此过程,直至仅剩下一个点

(不难证明第1步中每一个点入度都非0,第2步中一定存在一个环)

在此过程中,再另外维护一棵树:在第2.(2)中,环上的每一个点向新建的点连一条边权为其对应边(即环上指向该点的边)的边权的边,显然这是一棵内向树,且根即为最后剩下的节点

此时,每一个节点的答案即树上的边权和-其到根路径上的边权和

下面,问题即如何(快速)实现之前的过程——

先对每一个点的边集(指到该点的边)维护一个可并堆,显然即可支持1的查询和2.(2)的合并,2.(1)的修改也可以用懒标记实现

此时,考虑从任意一点dfs,由于每一个点入度都非0,那么不断选择"指向自己的边权最小的边",当重复访问一个节点时即可进行缩点,缩点后继续递归即可

另外,图并不一定强连通,这个问题可以通过加入边$(1,2,\infty),(2,3,\infty),...,(n,1,\infty)$?来解决,若最终某个点的答案为$\infty$????即说明不存在以该点为根的生成树,进而图即变为强连通的

综上,总复杂度为$o(n\log n)$?,可以通过

[hdu6990]Directed Minimum Spanning Tree
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define ll long long
 5 #define ull unsigned ll
 6 #define pil pair<int,ll>
 7 #define fi first
 8 #define se second
 9 vector<int>v[N<<1];
10 int V,t,n,m,x,y,z,rt[N],ls[N<<1],rs[N<<1],fa[N],st[N],vis[N];
11 ll tag[N<<1],Val[N];
12 ull ans[N];
13 pil val[N<<1],pos[N];
14 int find(int k){
15     if (k==fa[k])return k;
16     return fa[k]=find(fa[k]);
17 }
18 void upd(int k,ll x){
19     tag[k]+=x,val[k].se+=x;
20 }
21 void down(int k){
22     if (!tag[k])return;
23     if (ls[k])upd(ls[k],tag[k]);
24     if (rs[k])upd(rs[k],tag[k]);
25     tag[k]=0;
26 }
27 int New(int x,ll z){
28     int k=++V;
29     ls[k]=rs[k]=tag[k]=0;
30     val[k]=make_pair(x,z); 
31     return V;
32 }
33 int merge(int k1,int k2){
34     if ((!k1)||(!k2))return k1+k2;
35     if (val[k1].se>val[k2].se)swap(k1,k2);
36     down(k1);
37     rs[k1]=merge(rs[k1],k2);
38     swap(ls[k1],rs[k1]);
39     return k1;
40 }
41 void add(int x,int y,ll z){
42     rt[y]=merge(rt[y],New(x,z));
43 }
44 void del(int x){
45     down(rt[x]);
46     rt[x]=merge(ls[rt[x]],rs[rt[x]]);
47 }
48 void dfs(int x,ull s){
49     if (x<=n)ans[x]=s;
50     for(int i=0;i<v[x].size();i++)dfs(v[x][i],s-Val[v[x][i]]);
51 }
52 int main(){
53     scanf("%d",&t);
54     while (t--){
55         scanf("%d%d",&n,&m);
56         V=st[0]=ans[0]=0;
57         for(int i=1;i<=(n<<1);i++){
58             fa[i]=i;
59             rt[i]=vis[i]=0;
60             v[i].clear();
61         }
62         for(int i=1;i<=m;i++){
63             scanf("%d%d%d",&x,&y,&z);
64             add(x,y,z);
65         }
66         for(int i=1;i<=n;i++)add(i,i%n+1,1e14);
67         st[++st[0]]=1,vis[1]=1;
68         int nn=n;
69         while (1){
70             x=st[st[0]];
71             while ((rt[x])&&(find(val[rt[x]].fi)==x))del(x);
72             if (!rt[x])break;
73             y=find(val[rt[x]].fi);
74             Val[x]=val[rt[x]].se,del(x);
75             if (rt[x])upd(rt[x],-Val[x]);
76             if (!vis[y]){
77                 st[++st[0]]=y,vis[y]=1;
78                 continue;
79             }
80             nn++;
81             while (1){
82                 v[nn].push_back(x);
83                 fa[x]=nn,rt[nn]=merge(rt[nn],rt[x]);
84                 if (x==y){
85                     st[st[0]]=nn,vis[nn]=1;
86                     break;
87                 }
88                 x=st[--st[0]];
89             }
90         }
91         for(int i=1;i<nn;i++)ans[0]+=Val[i];
92         dfs(nn,ans[0]);
93         for(int i=1;i<=n;i++){
94             if (ans[i]>1e14)printf("-1\n");
95             else printf("%llu\n",ans[i]);
96         }
97     }
98     return 0;
99 } 
View Code

 

[hdu6990]Directed Minimum Spanning Tree

上一篇:Collections的一些常用方法


下一篇:composer包开发