Luogu P2766 最长不下降子序列问题

题链

分析

第一问\(n^2\)DP

\(f[i]\)表示以\(i\)为结尾的答案

第2,3问的方案显然一定要密铺,所以可以建图了

显然裂点

每次把\(f[j]=f[i]+1,a[j]\geq a[i]\)\((i,j)\)连起来即可

然后原点和\(1\)连,T和最大的\(f\)连即可

WA了一次:忘记\(a[j]\geq a[i]\)的条件

#include<bits/stdc++.h>
const int INF=1e9;
using namespace std;

const int N=2e6+5;
int n,a[N],cnt,to[N],nxt[N],he[N],w[N],d[N],f[N],S,T;
queue<int>q;
void add(int u,int v,int k) {
	to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
	w[cnt]=k;
}
void adde(int u,int v,int k) {
	add(u,v,k),add(v,u,0);
}
bool bfs() {
	memset(d,0,sizeof(d));
	q.push(S); d[S]=1;
	while(!q.empty()) {
		int u=q.front(); q.pop();
		for(int e=he[u];e;e=nxt[e]) {
			int v=to[e];
			if(w[e]&&!d[v]) {
				d[v]=d[u]+1;
				q.push(v);
			}
		}
	}
	return d[T]!=0;
}
int dfs(int u,int lim) {
	if(!lim) return 0;
	if(u==T) return lim;
	int ret=0;
	for(int e=he[u];e;e=nxt[e]) {
		int v=to[e];
		if(w[e]&&d[v]==d[u]+1) {
			int t=dfs(v,min(w[e],lim));
			if(t==0) d[v]=0;
				else {
					lim-=t;
					ret+=t;
					w[e]-=t;
					w[e^1]+=t;
				}
		}
	}
	return ret;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int ans=0; S=n+n+1,T=S+1,cnt=1;
	for(int i=1;i<=n;i++) {
		f[i]=1;
		for(int j=1;j<i;j++) {
			if(a[j]<=a[i]) f[i]=max(f[j]+1,f[i]);
		}
		ans=max(ans,f[i]);
	}
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) {
		adde(i,i+n,1);
		if(f[i]==1) adde(S,i,1);
		if(f[i]==ans) adde(i+n,T,1);
		for(int j=i+1;j<=n;j++) {
			if(a[i]<=a[j]&&f[j]==f[i]+1) adde(i+n,j,1);
		}
	}
	int Ans=0;
	while(bfs()) Ans+=dfs(S,INF);
	printf("%d\n",Ans);
	if(ans==1) {
		printf("%d\n",n);
		return 0;
	}
	adde(1,1+n,INF),adde(S,1,INF),adde(n,n+n,INF);
	if(f[n]==ans) adde(n+n,T,INF);
	while(bfs()) Ans+=dfs(S,INF);
	if(Ans>=INF) Ans-=INF,Ans++; 
	printf("%d\n",Ans);
	return 0;
}
posted @ 2021-06-08 13:25  wwwsfff  阅读(47)  评论(0)    收藏  举报