2229: [Zjoi2011]最小割(最小割树)
Description
小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割” 现给定一张无向图,小白有若干个形如“图中有多少对点它们的最小割的容量不超过x呢”的疑问,小蓝虽然很想回答这些问题,但小蓝最近忙着挖木块,于是作为仍然是小蓝的好友,你又有任务了。
Input
输入文件第一行有且只有一个正整数T,表示测试数据的组数。 对于每组测试数据, 第一行包含两个整数n,m,表示图的点数和边数。 下面m行,每行3个正整数u,v,c(1<=u,v<=n,0<=c<=106),表示有一条权为c的无向边(u,v) 接下来一行,包含一个整数q,表示询问的个数 下面q行,每行一个整数x,其含义同题目描述。
Output
对于每组测试数据,输出应包括q行,第i行表示第i个问题的答案。对于点对(p,q)和(q,p),只统计一次(见样例)。
两组测试数据之间用空行隔开。
Sample Input
1
5 0
1
0
5 0
1
0
Sample Output
10
【数据范围】
对于100%的数据 T<=10,n<=150,m<=3000,q<=30,x在32位有符号整数类型范围内。
图中两个点之间可能有多条边
【数据范围】
对于100%的数据 T<=10,n<=150,m<=3000,q<=30,x在32位有符号整数类型范围内。
图中两个点之间可能有多条边
解题思路:
最小割树,用于解决全局任意点间的最小割。
考虑对于全局任意两个点的最小割将图分成的两部分,假如说我们在这两部分之间画一条分界线。
那么在两端各取一点再跑最小割,这时再画分界线,若两者最小割容量不同,那么那么这两条分界线一定没有交集。
贪心地想,若其中一个更优那么一定会取。
若第二条比第一条更优,那么一定说明第一遍时的两个点在第二条分界线同侧。
同时也说明,第二条分界线后除第二遍的终点以外,还会有点与这个点的最小割形成的割集与第二个割集相同。
所以可以每次取两个点跑最小割,划线。
然后下次在线同侧取点跑最小割划线,在线两端的点一定以这个割集为割集,所以更新答案即可。
分治的最坏时间复杂度为$O(n^2)$的,再套个Dinic时间复杂度为$O(n^4m)$的,不过这都是最坏的。
后者达上限非常困难前者也不容易。所以还是$O(玄学)$比较合适。
代码:
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 const int oo=0x3f3f3f3f; 6 struct pnt{ 7 int hd; 8 int lyr; 9 int now; 10 bool vis; 11 }p[1000]; 12 struct ent{ 13 int twd; 14 int lst; 15 int vls; 16 int his; 17 }e[1000000]; 18 int cnt; 19 int n,m; 20 int s,t; 21 int app[200]; 22 int tmp[200]; 23 int ans[151][151]; 24 std::queue<int>Q; 25 void ade(int f,int t,int v) 26 { 27 cnt++; 28 e[cnt].twd=t; 29 e[cnt].vls=v; 30 e[cnt].his=v; 31 e[cnt].lst=p[f].hd; 32 p[f].hd=cnt; 33 return ; 34 } 35 bool Bfs(void) 36 { 37 while(!Q.empty())Q.pop(); 38 for(int i=1;i<=n;i++) 39 p[i].lyr=0; 40 p[s].lyr=1; 41 Q.push(s); 42 while(!Q.empty()) 43 { 44 int x=Q.front(); 45 Q.pop(); 46 for(int i=p[x].hd;i;i=e[i].lst) 47 { 48 int to=e[i].twd; 49 if(p[to].lyr==0&&e[i].vls>0) 50 { 51 p[to].lyr=p[x].lyr+1; 52 if(to==t) 53 return true; 54 Q.push(to); 55 } 56 } 57 } 58 return false; 59 } 60 int Dfs(int x,int fll) 61 { 62 if(x==t) 63 return fll; 64 for(int& i=p[x].now;i;i=e[i].lst) 65 { 66 int to=e[i].twd; 67 if(p[to].lyr==p[x].lyr+1&&e[i].vls>0) 68 { 69 int ans=Dfs(to,std::min(fll,e[i].vls)); 70 if(ans>0) 71 { 72 e[i].vls-=ans; 73 e[((i-1)^1)+1].vls+=ans; 74 return ans; 75 } 76 } 77 } 78 return 0; 79 } 80 int Dinic() 81 { 82 int ans=0; 83 while(Bfs()) 84 { 85 for(int i=1;i<=n;i++) 86 p[i].now=p[i].hd; 87 int dlt; 88 while(dlt=Dfs(s,oo)) 89 ans+=dlt; 90 } 91 return ans; 92 } 93 void dfs(int x) 94 { 95 if(p[x].vis) 96 return ; 97 p[x].vis=true; 98 for(int i=p[x].hd;i;i=e[i].lst) 99 { 100 int to=e[i].twd; 101 if(e[i].vls>0) 102 dfs(to); 103 } 104 return ; 105 } 106 void Build(int l,int r) 107 { 108 if(l==r) 109 return ; 110 s=app[l],t=app[r]; 111 for(int i=1;i<=cnt;i+=4) 112 { 113 e[i].vls=e[i].his; 114 e[i+1].vls=e[i+1].his; 115 e[i+2].vls=e[i+2].his; 116 e[i+3].vls=e[i+3].his; 117 } 118 int tmf=Dinic(); 119 for(int i=1;i<=n;i++) 120 p[i].vis=false; 121 dfs(s); 122 for(int i=1;i<=n;i++) 123 if(p[i].vis) 124 for(int j=1;j<=n;j++) 125 if(!p[j].vis) 126 ans[i][j]=ans[j][i]=std::min(ans[i][j],tmf); 127 int i=l-1,j=r+1; 128 for(int k=l;k<=r;k++) 129 if(p[app[k]].vis) 130 tmp[++i]=app[k]; 131 else 132 tmp[--j]=app[k]; 133 for(int k=l;k<=r;k++) 134 app[k]=tmp[k]; 135 Build(l,i); 136 Build(j,r); 137 return ; 138 } 139 int main() 140 { 141 // freopen("a.in","r",stdin); 142 int T; 143 scanf("%d",&T); 144 while(T--) 145 { 146 scanf("%d%d",&n,&m); 147 cnt=0; 148 for(int i=1;i<=n;i++) 149 app[i]=i, 150 p[i].hd=0; 151 memset(ans,0x3f,sizeof(ans)); 152 for(int i=1;i<=m;i++) 153 { 154 int a,b,c; 155 scanf("%d%d%d",&a,&b,&c); 156 ade(a,b,c); 157 ade(b,a,c); 158 } 159 Build(1,n); 160 int q; 161 scanf("%d",&q); 162 while(q--) 163 { 164 int x; 165 scanf("%d",&x); 166 int ansl=0; 167 for(int i=1;i<n;i++) 168 for(int j=i+1;j<=n;j++) 169 if(ans[i][j]<=x) 170 ansl++; 171 printf("%d\n",ansl); 172 } 173 puts(""); 174 } 175 return 0; 176 }