P2766 最长不下降子序列问题

通俗的,对于每个数只能取一次和取无限次,将点拆开加入边约束

因为让求最长上升子序列的个数,所以当一个数字被选择时,它必须是最长上升子序列的一部分

我们求出最长上升子序列的 \(DP\) 数组,当 \(x\) 可以连边 \(y\) 当且仅当 \(y\)\(DP\) 中可以从 \(x\) 转移过来

这样问题就显然了,我们只需要求解 \(DAG\) 上的最长链个数即可

具体的连边方法,设最长上升子序列长度为 \(k\)

  • 每个 \(x\)\(x'\) 连边

  • \(f_x = k\) 时,从 \(S\)\(x\) 连边

  • \(x < y\)\(f_x + 1 = f_y\)\(v_x < v_y\) 时, \(y'\)\(x\) 连边

  • \(f_x = 1时,\)x'$ 向 \(T\) 连边

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#include<cctype>
#include<cstring>
using namespace std;
namespace OI{
	#define rg register
	template <typename T>
	inline void read(T &x){
		x=0;
		static char ch;ch=getchar();
		static int f;f=0;
		while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
		while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
		x=f?-x:x;
	}
	template <typename T>
	inline int min(const T &a,const T &b){ return a<b?a:b; }
	template <typename T>
	inline int max(const T &a,const T &b){ return a>b?a:b; }
	const int N=1005,M=4005;
	int head[N],ver[M],nxt[M],flow[M],tot=1;
	inline void add(int &x,int &y,int z){
		ver[++tot]=y;
		flow[tot]=z;
		nxt[tot]=head[x];
		head[x]=tot;
	}
	inline void adds(int x,int y,int z){
		add(x,y,z);
		add(y,x,0);
	}
	int n,s,t;
	int dis[N],cur[N];
	int g[505];
	inline int bfs(){
		queue<int> q;
		memset(dis,0,sizeof dis);
		dis[s]=1;
		q.push(s);
		while(!q.empty()){
			int x=q.front();q.pop();
			cur[x]=head[x];
			for(int y,i=head[x];i;i=nxt[i]){
				y=ver[i];
				if(!dis[y]&&flow[i]){
					dis[y]=dis[x]+1;
					q.push(y);
				}
			}
		}
		return dis[t];
	}
	int dfs(int x,int f){
		if(x==t) return f;
		int used=0;
		for(int w,y,&i=cur[x];i;i=nxt[i]){
			y=ver[i];
			if(dis[y]==dis[x]+1&&flow[i]){
				w=dfs(y,min(f-used,flow[i]));
				if(w){
					flow[i]-=w;
					flow[i^1]+=w;
					used+=w;
					if(used==f) return f;
				}
			}
		}
		if(!used) dis[x]=0;
		return used;
	}
	inline int dinic(){
		int ans=0;
		while(bfs()) ans+=dfs(s,0x3f3f3f3f);
		return ans;
	}
	const int top=500;
	template <typename T>
	inline void ckmax(T &a,const T &b){
		if(a<b) a=b;
	}
	inline void modify(int x,int d){
		for(int i=x;i<=n;i+=(i&-i)) ckmax(g[i],d);
	}
	inline int ask(int x){
		int res=0;
		for(int i=x;i;i^=(i&-i)) ckmax(res,g[i]);
		return res;
	}
	int a[N],b[N],f[N];
	inline void main(){
		read(n);
		s=0;t=n<<1|1;
		for(int i=1;i<=n;++i) read(a[i]),b[i]=a[i];
		sort(b+1,b+1+n);
		b[0]=unique(b+1,b+1+n)-b-1;
		int res=0;
		for(int i=1;i<=n;++i){
			a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
			f[i]=ask(a[i])+1;
			ckmax(res,f[i]);
			modify(a[i],f[i]);
		}
		printf("%d\n",res);
		for(int i=1;i<=n;++i){
			if(f[i]==res){
				adds(s,(i<<1)-1,1);
			}
			if(f[i]==1){
				adds(i<<1,t,1);
			}
			adds((i<<1)-1,i<<1,1);
		}
		for(int i=1;i<=n;++i){
			for(int j=1;j<i;++j){
				if(a[i]>=a[j]&&f[i]==f[j]+1){
					adds(i<<1,(j<<1)-1,1);
				}
			}
		}
		printf("%d\n",dinic());
		for(int i=2;i<=tot;i=-~-~i){
			flow[i]+=flow[i^1];
			flow[i^1]=0;
		}
		adds(1,2,0x3f3f3f3f);
		adds((n<<1)-1,n<<1,0x3f3f3f3f);
		adds(2,t,0x3f3f3f3f);
		if(f[n]==res) adds(s,(n<<1)-1,0x3f3f3f3f);
		printf("%d\n",dinic());
	}
}
signed main(){ OI::main(); return 0; }
posted @ 2020-06-13 07:23  __int256  阅读(225)  评论(0编辑  收藏  举报