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;
}

浙公网安备 33010602011771号