题目描述

学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割。
而对冲刺NOI竞赛的选手而言,求带权图中两点的最小割已经不是什么难事了。我们可以把视野放宽,考虑有N个点的无向连通图中所有点对的最小割的容量,共能得到N(N−1)2个数值。
这些数值中互不相同的有多少个呢?这似乎是个有趣的问题。

输入

输入文件第一行包含两个数N,M,表示点数和边数。接下来M行,每行三个数u,v,w,表示点u和点v(从1开始标号)之间有条边权值是w。
1<=N<=850 1<=M<=8500 1<=W<=100000

输出

输出文件第一行为一个整数,表示个数。

样例输入

4 4
1 2 3
1 3 6
2 4 5
3 4 4

样例输出

3


题解

分治+最小割,同 bzoj2229

最后统计答案时把两点最小割取出来,去个重,求一下个数即可。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 860
#define M 17010
using namespace std;
queue<int> q;
int n , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N] , a[N] , tmp[N] , ans[N][N] , v[1000000] , tot;
void add(int x , int y , int z)
{
to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
to[++cnt] = x , val[cnt] = z , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
int x , i;
memset(dis , 0 , sizeof(dis));
while(!q.empty()) q.pop();
dis[s] = 1 , q.push(s);
while(!q.empty())
{
x = q.front() , q.pop();
for(i = head[x] ; i ; i = next[i])
{
if(val[i] && !dis[to[i]])
{
dis[to[i]] = dis[x] + 1;
if(to[i] == t) return 1;
q.push(to[i]);
}
}
}
return 0;
}
int dinic(int x , int low)
{
if(x == t) return low;
int temp = low , i , k;
for(i = head[x] ; i ; i = next[i])
{
if(val[i] && dis[to[i]] == dis[x] + 1)
{
k = dinic(to[i] , min(temp , val[i]));
if(!k) dis[to[i]] = 0;
val[i] -= k , val[i ^ 1] += k;
if(!(temp -= k)) break;
}
}
return low - temp;
}
void solve(int l , int r)
{
if(l >= r) return;
int i , j , sum = 0 , p1 , p2;
for(i = 2 ; i <= cnt ; i += 2) val[i] = val[i ^ 1] = (val[i] + val[i ^ 1]) >> 1;
s = a[l] , t = a[r];
while(bfs()) sum += dinic(s , 1 << 30);
for(i = 1 ; i <= n ; i ++ )
if(dis[i])
for(j = 1 ; j <= n ; j ++ )
if(!dis[j])
ans[i][j] = ans[j][i] = min(ans[i][j] , sum);
for(p1 = i = l , p2 = r ; i <= r ; i ++ )
{
if(dis[a[i]]) tmp[p1 ++ ] = a[i];
else tmp[p2 -- ] = a[i];
}
for(i = l ; i <= r ; i ++ ) a[i] = tmp[i];
solve(l , p2) , solve(p1 , r);
}
int main()
{
int m , i , j , x , y , z , ret = 0;
scanf("%d%d" , &n , &m);
while(m -- ) scanf("%d%d%d" , &x , &y , &z) , add(x , y , z);
for(i = 1 ; i <= n ; i ++ ) a[i] = i;
memset(ans , 0x7f , sizeof(ans)) , solve(1 , n);
for(i = 1 ; i <= n ; i ++ )
for(j = i + 1 ; j <= n ; j ++ )
v[++tot] = ans[i][j];
sort(v + 1 , v + tot + 1);
v[0] = -1 << 30;
for(i = 1 ; i <= tot ; i ++ )
if(v[i] != v[i - 1])
ret ++ ;
printf("%d\n" , ret);
return 0;
}

最新文章

  1. 微信小程序基础入门
  2. SQL 字符串处理大全
  3. 判断字符串的首字母 ---------startsWith
  4. Centos下 为Firefox安装Flash插件
  5. 用scala实现一个sql执行引擎-(下)
  6. Tomcat 内存和线程配置优化
  7. Intent官方教程(6)常见Intent示例,启动日历,时钟,镜头等。
  8. Codeforces Round #215 (Div. 1)
  9. SpEL快速入门
  10. WCF默认实例的解读
  11. 平时的笔记04:处理stagger模块
  12. springMVC入门配置及helloworld实例
  13. Google代码规范
  14. vue打开新页面
  15. git冲突Please move or remove them before you can merge
  16. Notepad++插件下载和介绍
  17. 在kubernetes中运行单节点有状态MySQL应用
  18. linux 链接命令
  19. GitHub个人使用入门
  20. 1.IPtable基础命令总结

热门文章

  1. Android 8.0 NotificationChannel 采坑实例
  2. iOS 锁的常用方法
  3. Nengo 神经网络
  4. MVC 学习小总结
  5. 树形DP 统计树中长度为K的路径数量——Distance in Tree
  6. 前端知识点总结——HTML
  7. Jordan 标准型定理
  8. PAT (Basic Level) Practise (中文)-1027. 打印沙漏(20)
  9. 【OS_Linux】三大文本处理工具之sed命令
  10. PHP操作MySQL事务实例