bzoj 3882: [Wc2015]K小割(最小割+优先队列)

3882: [Wc2015]K小割

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 79   Solved: 50
[ Submit][ Status][ Discuss]

Description

 

Input

Output

 

Sample Input

3 3 1 3 100
1 2 3
2 3 4
1 3 5

Sample Output

8
9
12
-1

HINT

Source

[ Submit][ Status][ Discuss]

PS:数据范围


题解:最小割+优先队列

这道题要想A掉需要写三个部分分。

part1:暴力枚举每条边割不割,保留所有合法的割,然后从小到大排序输出

时间复杂度O(2^m*n),可以过前两个点,因为前两个点的K较大,所有不能与下面的part3合并

part2:优先队列

7-14这几个点很有规律,对于除s,t外的每个点都分别有且仅有一条边与s,t相连。那么对于每一个点都必须至少割掉其中的一条边。设两条边的权值为a,b(a<b),那么对于每个点的选择有{a,b,a+b} 三种,我们可以通过对于每个点选择的升级/降级,来达到不同的状态。

我们将点以(b-a)为第一关键字(从第一级升到第二级的代价),a为第二关键字排序(从第二级升到第三级的代价)。

对于每个状态我们有三种选择

(1)将当前点的状态升级

(2)当前点保持不变,将该点的后一个点升级,并将当前点改为他后面的点。

(3)将当前点降级,将该点的后一个点升级,并将当前点改为他后面的点(需要注意的是在降级的时候只能是从b->a,不能是从a+b->b,否则会出现重复的状态,因为a+b->b的改变其实可以转化成选择(2))

总之在更新状态的时候需要时刻注意,不要更新到之前已经得到的状态,如果开始后一个点的升级,那么当前点就不能再改变了。

part3:最小割

剩下的部分分可以统一用这种方法解决。

我们先用最大流得到最小割,然后考虑怎么在最小割的基础上得到次小割。

次小割有两种产生的方式:

(1)强制割集中的某条边不选,然后求此时的最小割

(2)选择不再割集中的某条边,然后割掉。

对于第(2)种产生方式,必然是选择不在割集中的边权最小的边来更新答案。那么我们如何确定割集呢?从s开始走剩余流量不为0的边,将所有能遍历到的点打访问标记,如果一条边的两个端点x[i]有标记,y[i]没有标记,那么该边在最小割的割集中。

第一种情况,对于每一条边来说,如果这条边不割,那么s->x[i],y[i]->t都其中一个必须不连通,我们可以在做完最小割的残量网络上对于s->x[i],y[i]->t分别求最小割,然后从中选取较小的更新答案。

处理完(1)(2)两种产生方式后,得到的答案就是针对当前割集的次小割。

然后我们需要用类似k短路的方式进行搜索,每次可以强制产生次小割的边选与不选,进而得到更多的割集,依次取出前k个就是答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#define N 150003
#define M 3003
#define LL long long 
using namespace std;
const LL inf=1e13;
struct data{
	int now,opt; LL val;
	bool operator <(data a) const{
	  return a.val<val;
	}
};
struct node{
    LL a,b;
}e[N];
int n,m,s,t,k,x[N],y[N],c[N],vis[N],fa[N],cnt,mark[20];
int point[N],v[N],nxt[N],num[N],tot;
LL ans[N*10];
int cmp(node a,node b)
{
	return a.a<b.a||a.a==b.a&&a.b<b.b;
}
void add(int x,int y,int z)
{
	tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; num[tot]=z;
}
bool check()
{
   memset(mark,0,sizeof(mark));
   queue<int> p; p.push(s); mark[s]=1;
   while (!p.empty()) {
   	int now=p.front(); p.pop();
   	for (int i=point[now];i;i=nxt[i]) {
   		 if (vis[num[i]]||mark[v[i]]) continue;
   		 mark[v[i]]=1;
   		 p.push(v[i]);
	   }
   }
   return !mark[t];
}
void dfs(int x,LL sum)
{
	if (x==m+1) {
		if (check()) 
		 ans[++ans[0]]=sum;
		return;
	}
	vis[x]=1; dfs(x+1,sum+c[x]);
	vis[x]=0; dfs(x+1,sum);
}
struct graph{
	#define N1 53 
    int tot,point[N1],deep[N1],num[N1],cur[N1],mark[M];
    int nxt[M],v[M],last[M]; LL remain[M];
    void clear() {
    	tot=-1;
    	memset(point,-1,sizeof(point));
    	memset(num,0,sizeof(num));
	}
    void add(int x,int y,LL z)
	{
		tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
		tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
	//	cout<<x<<" "<<y<<" "<<z<<endl;
	}
	LL addflow(int s,int t)
	{
	    LL ans=inf; int now=t;
		while (now!=s) {
			ans=min(ans,remain[last[now]]);
			now=v[last[now]^1];
		}
		now=t;
		while (now!=s) {
			remain[last[now]]-=ans;
			remain[last[now]^1]+=ans;
			now=v[last[now]^1];
		}
		return ans;
	}
	void bfs(int s,int t)
	{
		for (int i=1;i<=n;i++) deep[i]=n;
		queue<int> p; p.push(t); deep[t]=0;
		while (!p.empty()) {
			int now=p.front(); p.pop();
			for (int i=point[now];i!=-1;i=nxt[i])
			 if (deep[v[i]]==n&&remain[i^1])
			  deep[v[i]]=deep[now]+1,p.push(v[i]);
		}
	}
	LL isap(int s,int t)
	{
		LL ans=0; int now=s;
	    bfs(s,t);
	    memset(num,0,sizeof(num));
	    for (int i=1;i<=n;i++) num[deep[i]]++;
		for (int i=1;i<=n;i++) cur[i]=point[i];
		while (deep[s]<n) {
			if (now==t) {
				ans+=addflow(s,t);
				now=s;
			}
			bool pd=false;
			for (int i=cur[now];i!=-1;i=nxt[i])
			 if (deep[v[i]]+1==deep[now]&&remain[i]) {
			 	cur[now]=i;
			 	last[v[i]]=i;
			 	pd=true;
			 	now=v[i];
			 	break;
			 }
			if (!pd) {
				int minn=n;
				for (int i=point[now];i!=-1;i=nxt[i])
				 if (remain[i]) minn=min(minn,deep[v[i]]);
				if (!--num[deep[now]]) break;
				num[deep[now]=minn+1]++;
				cur[now]=point[now];
				if (now!=s) now=v[last[now]^1];
			}
		} 
		return ans; 
	}
	void dfs(int x)
	{
		vis[x]=1;
		for (int i=point[x];i!=-1;i=nxt[i])
		 if (!vis[v[i]]&&remain[i]) dfs(v[i]);
	}
	void check() {
		memset(mark,0,sizeof(mark));
		memset(vis,0,sizeof(vis));
		dfs(s);
		for (int i=1;i<=m;i++)
		 if (vis[x[i]]&&!vis[y[i]]) mark[i]=1;
 	}
}T,t1,t2;
struct qu
{
	bool able[M],uab[M],pds[M],pdt[M];
	LL mins[53],mint[53],val;
    int now;
	bool operator <(qu a) const {
	  return a.val<val;
	}
	void build(){
		t1=T; val=0;
		for (int i=1;i<=m;i++) {
			int u1=2*(i-1); int u2=u1+1;
			if (able[i])  val+=(LL)c[i],t1.remain[u1]=0,t1.remain[u2]=0;
			else if (uab[i]) t1.remain[u1]=inf,t1.remain[u2]=0;
		}
		//for (int i=1;i<=m;i++) cout<<able[i]<<" ";
		//cout<<endl;
		//for (int i=1;i<=m;i++) cout<<uab[i]<<" ";
		//cout<<endl;
		val+=t1.isap(s,t); t1.check();
	//	for (int i=1;i<=m;i++) cout<<t1.mark[i]<<" "; cout<<endl;
		LL ansx;  ansx=inf; now=0;
		memset(pds,0,sizeof(pds)); memset(pdt,0,sizeof(pdt));
		for (int i=1;i<=m;i++) {
			if (able[i]||uab[i]) continue;
			if (t1.mark[i]) {
				if (!pds[x[i]]) {
				 pds[x[i]]=1,t2=t1;
				// for (int j=1;j<=n;j++)
				  //for (int k=t2.point[j];k!=-1;k=t2.nxt[k]) cout<<t2.remain[k]<<endl;
				 mins[x[i]]=t2.isap(s,x[i]);
			    }
				if (!pdt[y[i]]) pdt[y[i]]=1,t2=t1,mint[y[i]]=t2.isap(y[i],t);
				if (ansx>mins[x[i]]) ansx=mins[x[i]],now=i;
				if (ansx>mint[y[i]]) ansx=mint[y[i]],now=i;
			}
			else if (c[i]<ansx) ansx=c[i],now=i;
		}
		val+=ansx;
	}
}a,cur,cur1;
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d%d%d%d",&n,&m,&s,&t,&k);
	if (n<=10&&m<=20) {
		cnt=0;
		for (int i=1;i<=m;i++) 
		 scanf("%d%d%d",&x[i],&y[i],&c[i]),add(x[i],y[i],i);
		dfs(1,0); cnt=ans[0];
		sort(ans+1,ans+cnt+1);
		//for (int i=1;i<=cnt;i++) cout<<ans[i]<<" ";
		//cout<<endl;
		for (int i=1;i<=min(cnt,k);i++)  printf("%lld\n",ans[i]);
		if (k>cnt) printf("-1\n");
		return 0;
	} else
	if (m==2*n-4) {
		for (int i=1;i<=m;i++) {
			int x,y,z; scanf("%d%d%d",&x,&y,&z);
			if (x>y) swap(x,y);
			if (x==1) e[y-1].a=(LL)z;
			if (y==n) e[x-1].b=(LL)z;
		}
		LL ans=0;
		for (int i=1;i<=n;i++) {
		 if (e[i].a>e[i].b) swap(e[i].a,e[i].b);
		 ans+=e[i].a;
		 LL t=e[i].a;
		 e[i].a=e[i].b-e[i].a;
		 e[i].b=t;
	    }
		sort(e+1,e+n-1,cmp);
		printf("%lld\n",ans); k--;
		//for (int i=1;i<=n;i++) cout<<e[i].a<<" "<<e[i].b<<endl;
		data cur; cur.now=1; cur.opt=1; cur.val=ans+e[1].a; 
		priority_queue<data> p; p.push(cur);
		int size=0;
		while (!p.empty()) {
			cur=p.top(); p.pop();
			printf("%lld\n",cur.val); size++;
			if (size==k) break;
			data nt; nt=cur; 
			if (nt.opt<2) {
				nt.opt++; 
				nt.val+=e[nt.now].b;
				p.push(nt);
			}
			nt=cur;
			if (nt.now!=n-2) {
				nt.now++;
				nt.opt=1;
				nt.val+=e[nt.now].a;
				p.push(nt);
			}
			nt=cur;
			if (nt.opt==1&&nt.now<n-2) {
				nt.val-=e[nt.now].a;
				nt.val+=e[nt.now+1].a;
				nt.opt=1;
				nt.now++;
				p.push(nt);
			}
		}
		if (k>size)printf("-1\n");
		return 0;
	} else
	{
		T.clear();
		for (int i=1;i<=m;i++) {
			scanf("%d%d%d",&x[i],&y[i],&c[i]);
			T.add(x[i],y[i],(LL)c[i]);
		}
		t1=T; printf("%lld\n",t1.isap(s,t)); k--;
		a.build(); 
		priority_queue<qu> p; p.push(a);
		while (!p.empty()) {
			cur=cur1=p.top(); p.pop();
			if (cur.val<inf)  printf("%lld\n",cur.val),k--;
			else break;
			if (!k||!cur.now) break;
			cur.able[cur.now]=1; cur.build(); 
			if (cur.val<inf) 
			 p.push(cur);
			cur1.uab[cur1.now]=1; cur1.build();
			if (cur1.val<inf) 
			 p.push(cur1);
		}
		if (k) printf("-1\n");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值