• 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; }
    
  • 相关阅读:
    [CF666E] Forensic Examination
    [BZOJ3739] DZY loves math VIII
    [BZOJ3561] DZY Loves Math VI
    VS中的类模板
    php中的this,self,parent
    js中的 Table 对象
    CEF中弹出窗口的处理
    VC禁止或允许拖拽改变窗口尺寸
    MFC系统自动生成的停靠窗格关掉后,如何重新显示?
    VS2010 如何自动生成UML图
  • 原文地址:https://www.cnblogs.com/int256/p/13111513.html
Copyright © 2020-2023  润新知