【分治最小割】[CQOI2016]不同的最小割
题目描述
分析
一下所称的两点之间的最小割是指以其中一点为源点,另一个点为汇点的最小割,因为是无向图,交换源点、汇点之后最小割的值不变
暴力的做法
枚举点对,求出所有点对的最小割,然后全部排序看有多少个不同的值。
在暴力的基础上优化
我们能不能更快地求出所有点对的的最小割呢?
分治最小割
引入
根据一些结论,最小割最多有n-1个,这n-1个最小割构成一个最小割树(我也不知道为什么)
然后,就可以分治求最小割了
做法
当前状态下需要处理的点集为V,从中任意选择两点,任意找两点为源点,汇点跑最大流,然后将整个图和V集合分为S集,T集,更新整个图中两个点分别在S集,T集的点对的最小割。
然后递归处理V集合中的S集,T集。
这道题
如果求出每个点对的最小割也很慢,不过显然,最小割的数值都是在分治过程中找到的,只需要把分治过程中找到的数值扔进set,最后看set的大小就可以了
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<set>
#define MAXN 850
#define MAXM 8500
#define INF 0x7fffffff
using namespace std;
set<int>se;
int n,m,tot,S,T,dist[MAXN+10],vd[MAXN+10],ne,a[MAXN+10],tmp[MAXN+10];
long long c[MAXN+10][MAXN+10],b[MAXN*MAXN+10];
bool vis[MAXN+10];
queue<int>q;
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
struct node{
int v,cap;
node *next,*back;
}*adj[MAXN+10],edge[MAXM*2+10],edge2[MAXM*2+10],*ecnt=edge;
inline void addedge(int u,int v,int cap){
node *p=++ecnt;
p->v=v;
p->cap=cap;
p->next=adj[u];
adj[u]=p;
p=p->back=++ecnt;
p->v=u;
p->cap=cap; //无向图
p->next=adj[v];
adj[v]=p;
p->back=ecnt-1;
}
void read(){
Read(n),Read(m);
int i,u,v,wt;
tot=n;
for(i=1;i<=m;i++){
Read(u),Read(v),Read(wt);
addedge(u,v,wt);
}
ne=ecnt-edge;
for(i=1;i<=ne;i++)
edge2[i]=edge[i];
for(i=1;i<=n;i++)
a[i]=i;
}
void spfa(int S){
int i,u;
for(i=1;i<=tot;i++)
dist[i]=INF;
q.push(S);
dist[S]=0;
while(!q.empty()){
u=q.front();
q.pop();
vis[u]=0;
for(node *p=adj[u];p;p=p->next){
if(p->back->cap&&dist[p->v]>dist[u]+1){
dist[p->v]=dist[u]+1;
if(!vis[p->v]){
vis[p->v]=1;
q.push(p->v);
}
}
}
}
}
long long dfs(int u,long long augu){
if(u==T)
return augu;
long long v,augv=0,delta;
int mind=tot-1;
for(node *p=adj[u];p;p=p->next){
if(p->cap){
v=p->v;
if(dist[u]==dist[v]+1){
delta=min(augu-augv,(long long)p->cap);
delta=dfs(p->v,delta);
p->cap-=delta;
p->back->cap+=delta;
augv+=delta;
if(augu==augv||dist[S]>=tot)
return augv;
}
mind=min(dist[p->v],mind);
}
}
if(!augv){
if(!--vd[dist[u]])
dist[S]=tot;
vd[dist[u]=mind+1]++;
}
return augv;
}
long long sap(){
int i;
long long flow=0;
for(i=1;i<=ne;i++)
edge[i]=edge2[i];
for(i=1;i<=tot;i++)
vd[i]=0,vis[i]=0;
spfa(T);
for(i=1;i<=tot;i++){
if(dist[i]==INF)
dist[i]=tot;
vd[dist[i]]++;
}
while(dist[S]<tot)
flow+=dfs(S,INF);
return flow;
}
void bfs(int S){
q.push(S);
vis[S]=1;
int u;
while(!q.empty()){
u=q.front();
q.pop();
for(node *p=adj[u];p;p=p->next){
if(p->cap&&!vis[p->v]){
vis[p->v]=1;
q.push(p->v);
}
}
}
}
void partition(int l,int r){
if(l==r)
return;
S=a[l],T=a[r];
long long flow=sap(),ll=l,rr=r,i;
bfs(S);
for(i=l;i<=r;i++){
if(vis[a[i]])
tmp[ll++]=a[i];
else
tmp[rr--]=a[i];
}
for(i=l;i<=r;i++)
a[i]=tmp[i];
se.insert(flow);
partition(l,ll-1),partition(rr+1,r);
}
void print(){
printf("%d\n",se.size());
}
int main()
{
memset(c,0x7f,sizeof c);
read();
partition(1,n);
print();
}