P3308 [SDOI2014]LIS 最小割+可行边判断

题意:

戳这里

分析:

首先,我们把问题拆开来考虑

  1. 删掉一些项使得最长上升子序列变短,那么删掉的项一定在原来最长的 (LIS) 里面,删掉某些东西的最小代价让我们很容易想到最小割,最小代价就可以求出来了,第一问解决

  2. 求一组方案使得字典序最小,求出所有的方案再找字典序最小的那一种显然复杂度不对劲,那我们只能构造字典序最小的那一组,然后判断是否可行,也就是说我们要判断一条边是否是一条可行边,也就是是否属于一个最小割,这就是一个经典问题呀(具体题目可以参考 AHOI Mincut )

  • 对于最小割的可行边必经边 (u o v) ,都满足这条边满流, (u o v) 不存在新的增广路径,但是必须边还满足 残量网络中,u,v 不属于同一个 SCC

放到这个题上来说就是,我们按照 (c) 从小到大枚举边,判断它是否是可行边,如果是,为了防止它对其他边选择的影响,反着跑 dinic 退流,具体来说就是原来是 (s o in(e)) 有一些流量,那么现在就从 (in(e) o s)) 增广,把原来加的流量都删掉,使得原来一些满流的边不在满流,然后把枚举的这条边的流量置为 (0),还是不影响它作为最小割边集里的一条边的性质

tip:

按照讨论区的反馈和自身经历,这题有些卡常,每次都是 (1.05s) 最后手写队列提到了 (0.6) 秒左右

代码:

#include<bits/stdc++.h>
#define in(rt) rt<<1
#define out(rt) rt<<1|1
#define reg register 
#define inl inline 
using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 1505;
	const int maxm = 1e6+5;
	const int inf = 0x3f3f3f3f;
	int ti,n,m,cnt,num,S,T,l,r;
	int dp[maxn],dep[maxn<<1],cur[maxn<<1],head[maxn<<1],ans[maxn],pos[maxn],qu[maxn];
	struct node
	{
		int a,b,c,id;
		bool operator <(const node &b)const
		{
			return c<b.c;
		}
	}t[maxn];
	
	struct edge
	{
		int to,nxt,w;
	}e[maxm];
	
	inl void add(int u,int v,int w)
	{
		e[++cnt].to=v;
		e[cnt].w=w;
		e[cnt].nxt=head[u];
		head[u]=cnt;
	}
	
	inl void add_edge(int u,int v,int w)
	{
		add(u,v,w);add(v,u,0);
	}
	
	bool bfs(int st,int ed)
	{
		for(int i=0;i<=(n<<1)+2;i++) dep[i]=-1,cur[i]=head[i];
		l=r=0;
		qu[r++]=st;
		dep[st]=0;
		while(l<r)
		{
			int u=qu[l++];
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				if(e[i].w&&dep[v]==-1)
				{
					dep[v]=dep[u]+1;
					if(v==ed) return true;
					qu[r++]=v;
				}
			}
		}
		return dep[ed]!=-1;
	}
	
	int dfs(int u,int flow,int ed)
	{
		if(u==ed) return flow;
		int used=0,w;
		for(int &i=cur[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(e[i].w&&dep[v]==dep[u]+1)
			{
				w=dfs(v,min(e[i].w,flow-used),ed);
				e[i].w-=w;
				e[i^1].w+=w;
				used+=w;
				if(used==flow) return flow;
			}
		}
		if(!used) dep[u]=-1;
		return used;
	}
	
	inl int dinic(int st,int ed)
	{
		int res=0;
		while(bfs(st,ed)) res+=dfs(st,inf,ed);
		return res;
	}
	
	void work()
	{
		ti=read();
		while(ti--)
		{
			n=read();cnt=1;S=0;T=(n<<1)+2;num=m=0;
			for(reg int i=1;i<=n;i++) t[i].a=read(),t[i].id=i;
			for(reg int i=1;i<=n;i++) t[i].b=read();
			for(reg int i=1;i<=n;i++) t[i].c=read();
			for(reg int i=1;i<=n;i++)
			{
				dp[i]=1;
				for(reg int j=1;j<i;j++)
				{
					if(t[i].a>t[j].a) dp[i]=max(dp[i],dp[j]+1);
				}
				m=max(m,dp[i]);
			}
			for(reg int i=1;i<=n;i++) 
			{
				add_edge(in(i),out(i),t[i].b);pos[i]=cnt;
				if(dp[i]==1) add_edge(S,in(i),inf);
				else if(dp[i]==m) add_edge(out(i),T,inf);
				for(reg int j=i+1;j<=n;j++) if(dp[j]==dp[i]+1&&t[j].a>t[i].a) add_edge(out(i),in(j),inf);
			}
			printf("%d ",dinic(S,T));
			sort(t+1,t+n+1);
			for(reg int i=1;i<=n;i++)
			{
				int id=t[i].id;
				if(!bfs(in(id),out(id)))
				{
					ans[++num]=id;
					while(bfs(T,out(id))) dfs(T,inf,out(id));
					while(bfs(in(id),S)) dfs(in(id),inf,S);
					e[pos[id]].w=e[pos[id]^1].w=0;
				}
			}
			printf("%d
",num);
			sort(ans+1,ans+num+1);
			for(reg int i=1;i<=num;i++) printf("%d ",ans[i]);
			puts("");
			for(reg int i=0;i<=T;i++) head[i]=0;
		}
	
	}

}

int main()
{
	zzc::work();
	return 0;
}





原文地址:https://www.cnblogs.com/youth518/p/14270282.html