BZOJ4519[Cqoi2016]不同的最小割——最小割树+map
题目描述
学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成
两个部分,如果结点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
1 2 3
1 3 6
2 4 5
3 4 4
样例输出
3
最小割树模板题,暴力枚举任意两个点在最小割树上求路径边权最小值,用$map$存一下权值即可。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define pr pair<int,int> using namespace std; int head[900]; int to[1800]; int val[1800]; int next[1800]; int dep[900]; int tot; int f[900][12]; int g[900][12]; int n,m,k; int u,v,w; int id[900]; int now; map<int,int>mp; void add_edge(int u,int v,int w) { next[++tot]=head[u]; head[u]=tot; to[tot]=v; val[tot]=w; } namespace DINIC { int tot=1; int S,T; int q[900]; int d[900]; int head[900]; int next[18000]; int val[18000]; int col[900]; int to[18000]; int vir[18000]; void add(int u,int v,int w) { next[++tot]=head[u]; head[u]=tot; to[tot]=v; val[tot]=w; vir[tot]=w; } bool bfs(int S,int T) { int l=0,r=0; memset(d,-1,sizeof(d)); q[r++]=S; d[S]=0; while(l<r) { int now=q[l]; l++; for(int i=head[now];i;i=next[i]) { if(d[to[i]]==-1&&val[i]) { d[to[i]]=d[now]+1; q[r++]=to[i]; } } } if(d[T]==-1) { return false; } else { return true; } } int dfs(int x,int maxflow) { if(x==T) { return maxflow; } int nowflow; int used=0; for(int i=head[x];i;i=next[i]) { if(d[to[i]]==d[x]+1&&val[i]) { nowflow=dfs(to[i],min(maxflow-used,val[i])); val[i]-=nowflow; val[i^1]+=nowflow; used+=nowflow; if(nowflow==maxflow) { return maxflow; } } } if(used==0) { d[x]=-1; } return used; } int dinic() { for(int i=2;i<=tot;i++) { val[i]=vir[i]; } int res=0; while(bfs(S,T)==true) { res+=dfs(S,0x3f3f3f3f); } return res; } void find(int x) { col[x]=now; for(int i=head[x];i;i=next[i]) { if(col[to[i]]!=now&&val[i]) { find(to[i]); } } } void build(int l,int r) { if(l>=r) { return ; } S=id[l],T=id[l+1]; int cut=dinic(); now++; find(S); int L=l,R=r; for(int i=l;i<=r;i++) { if(col[id[i]]==now) { q[L++]=id[i]; } else { q[R--]=id[i]; } } for(int i=l;i<=r;i++) { id[i]=q[i]; } add_edge(S,T,cut); add_edge(T,S,cut); build(l,L-1); build(R+1,r); } }; void dfs(int x) { for(int i=1;i<=10;i++) { f[x][i]=f[f[x][i-1]][i-1]; g[x][i]=min(g[x][i-1],g[f[x][i-1]][i-1]); } for(int i=head[x];i;i=next[i]) { if(to[i]!=f[x][0]) { dep[to[i]]=dep[x]+1; f[to[i]][0]=x; g[to[i]][0]=val[i]; dfs(to[i]); } } } int lca(int x,int y) { int res=1<<30; if(dep[x]<dep[y]) { swap(x,y); } int d=dep[x]-dep[y]; for(int i=0;i<=10;i++) { if(d&(1<<i)) { res=min(res,g[x][i]); x=f[x][i]; } } if(x==y) { return res; } for(int i=10;i>=0;i--) { if(f[x][i]!=f[y][i]) { res=min(res,g[x][i]); res=min(res,g[y][i]); x=f[x][i]; y=f[y][i]; } } res=min(min(g[x][0],g[y][0]),res); return res; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); DINIC::add(u,v,w); DINIC::add(v,u,w); } for(int i=1;i<=n;i++) { id[i]=i; } DINIC::build(1,n); dfs(1); int ans=0; for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { if(!mp[lca(i,j)]) { mp[lca(i,j)]=1; ans++; } } } printf("%d",ans); }