求最长不下降子序列,每个数只能取一次时最多能拼出多少最长不下降子序列,第111个数和第nnn个数能用无限次其它的只能用一次时最多能拼出多少最长不下降子序列。
第一问就略过吧。
因为每个数只能用一次,所以可以把点拆开,设原点为iii,那么现在拆出iai_aia和ibi_bib。
考虑到最长上升子序列长度固定,我们又算出了f[i]f[i]f[i],所以我们可以根据f[i]f[i]f[i]来分层。
那么建边就是这样的:
- 源点和所有f[i]=1f[i]=1f[i]=1的iai_aia连边,流量为111
- 所有iai_aia和ibi_bib连边,流量为111
- 所有f[i]=maxf[i]=maxf[i]=max的ibi_bib和汇点连边,流量为111
这样就可以了。
第三问只让111和nnn无限使用,考虑到上面的图控制使用次数的边就是这个点拆出的两个点之间的流量,所以我们把1a1_a1a和1b1_b1b之间的流量改为无限,nan_ana和nbn_bnb之间的流量改为无限,SSS和1a1_a1a之间的流量改为无限,如果f[n]=maxf[n]=maxf[n]=max,那么就把nbn_bnb和TTT之间的流量改为无限。
code:code:code:
#include <bits/stdc++.h>
int n;
int max;
int S=1,T=2;
int a[1000000];
int f[1000000];
int head[3000],tot;
int cur[3000];
int deep[3000];
struct edge{
int to;
int nxt;
int flow;
}e[1000000];
std::queue<int>q;
void add(int x,int y,int flow){
e[++tot]={y,head[x],flow};
head[x]=tot;
e[++tot]={x,head[y],0};
head[y]=tot;
}
bool bfs(){
memset(deep,0,sizeof deep);
deep[S]=1;
q.push(S);
while(!q.empty()){
int X=q.front();
q.pop();
for(int i=head[X];i;i=e[i].nxt){
int y=e[i].to;
if(!deep[y]&&e[i].flow){
deep[y]=deep[X]+1;
q.push(y);
}
}
}
return deep[T];
}
int dfs(int x,int flow){
if(x==T||!flow)
return flow;
int Flow=0;
for(int &i=cur[x];i;i=e[i].nxt){
int y=e[i].to;
if(e[i].flow&&deep[y]==deep[x]+1){
if(int w=dfs(y,std::min(flow,e[i].flow))){
e[i].flow-=w;
e[i^1].flow+=w;
Flow+=w;
flow-=w;
if(!flow)
break;
}
}
}
return Flow;
}
void dinic(){
int maxflow=0;
while(bfs()){
memcpy(cur,head,sizeof head);
while(int w=dfs(S,0x3f3f3f3f))
maxflow+=w;
}
printf("%d\n",maxflow);
add(S,3,0x3f3f3f3f);
add(3,3+n,0x3f3f3f3f);
if(f[n]==max){
add(2+2*n,T,0x3f3f3f3f);
add(2+n,2+2*n,0x3f3f3f3f);
}
while(bfs()){
memcpy(cur,head,sizeof head);
while(int w=dfs(S,0x3f3f3f3f))
maxflow+=w;
}
printf("%d\n",maxflow);
}
main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
f[1]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j)
if(a[i]>=a[j])
f[i]=std::max(f[i],f[j]+1);
max=std::max(max,f[i]);
}
for(int i=1;i<=n;++i)
if(f[i]==1)
add(S,2+i,1);
for(int i=1;i<=n;++i){
add(2+i,2+n+i,1);
for(int j=1;j<i;++j){
if(a[j]<=a[i]&&f[j]+1==f[i])
add(2+j+n,2+i,1);
}
}
for(int i=1;i<=n;++i)
if(f[i]==max)
add(2+n+i,T,1);
printf("%d\n",max);
dinic();
return 0;
}