网络流24题(六)
网络流24题(六)
六、最长不下降子序列
题目描述
给定正整数序列 \(x_1 \ldots, x_n\)
- 计算其最长不下降子序列的长度 ss。
- 如果每个元素只允许使用一次,计算从给定的序列中最多可取出多少个长度为 ss 的不下降子序列。
- 如果允许在取出的序列中多次使用 \(x_1\) 和 \(x_n\)(其他元素仍然只允许使用一次),则从给定序列中最多可取出多少个不同的长度为 \(s\) 的不下降子序列。
令 \(a_1, a_2, \ldots, a_s\)为构造 \(S\) 时所使用的下标,\(b_1, b_2, \ldots, b_s\) 为构造 \(T\) 时所使用的下标。且 \(\forall i \in [1,s-1]\),都有 \(a_i \lt a_{i+1},b_i \lt b_{i+1}\)。则 \(S\) 和 \(T\) 不同,当且仅当 \(\exists i \in [1,s]\),使得 \(a_i \neq b_i\)。
输入格式
第一行有一个正整数 \(n\),表示给定序列的长度。接下来的一行有 \(n\) 个正整数\(x_1, ..., x_n\)。
输出格式
- 第 1 行是最长不下降子序列的长度 \(s\)。
- 第 2 行是可取出的长度为 \(s\) 的不下降子序列个数。
- 第 3 行是允许在取出的序列中多次使用 \(x_1\) 和 \(x_n\) 时可取出的长度为 \(s\) 的不同的不下降子序列个数。
题解
模型:
最大流,分层图,拆点
建图与实现:
对于问题一,\(dp\)即可,答案为\(len\)。
对于问题二,就利用问题一得到的\(f[i]\)分层,当\(f[i] = f[j]+1\) && \(a[i] < a[j]\) && \(i < j\),连接一条有向边,容量为1。
这样一来,任意一条从\(f[1]\)的点到\(f[len]\)的路径都是满足的,跑最大流即可。但是注意有结点只可以使用一次的限制,可以理解为结点容量。在这种情况下,可以选择拆点,容量为1。
对于问题三,只需要处理一下第一个点和最后一个点就行,但是题目似乎有一个点是错的,只有一个点的情况应该是可以无线用的,我下下来特判一下。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N = 1050,M = 2e5+5,inf = 0x3f3f3f3f;
ll head[N],cnt = 1;
struct Edge{ll to,w,nxt;}edge[M];
void add(ll u,ll v,ll w){
edge[++cnt].to = v;
edge[cnt].w = w;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
ll n,m,s,t,lv[N],cur[N];
bool bfs(){
memset(lv, -1, sizeof(lv));
lv[s] = 0;
memcpy(cur, head, sizeof(head));
queue<int> q;q.push(s);
while (!q.empty()){
int p = q.front();q.pop();
for (ll eg = head[p]; eg; eg = edge[eg].nxt){
ll to = edge[eg].to, vol = edge[eg].w;
if (vol > 0 && lv[to] == -1)lv[to] = lv[p] + 1, q.push(to);
}
}
return lv[t] != -1;
}
ll dfs(ll p = s, ll flow = inf){
if (p == t)return flow;
int rmn = flow;
for (ll &eg = cur[p]; eg; eg = edge[eg].nxt){
if (!rmn)break;
int to = edge[eg].to, vol = edge[eg].w;
if (vol > 0 && lv[to] == lv[p] + 1){
ll c = dfs(to, min(vol, rmn));
rmn -= c;
edge[eg].w -= c;
edge[eg ^ 1].w += c;
}
}
return flow - rmn;
}
ll dinic(){
ll ans = 0;
while(bfs()) ans += dfs();
return ans;
}
ll a[N],f[N],len = -1;
void dp(){
for(ll i = 1;i <= n;i++) {
for (ll j = 1; j < i; j++) {
if (a[i] >= a[j]) f[i] = max(f[i], f[j] + 1);
}
len = max(len, f[i]);
}
cout<<len<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(ll i = 1;i <= n;i++)cin>>a[i],f[i] = 1;
if(n == 1) {
cout<<1<<endl<<1<<endl<<1<<endl;
return 0;
}
dp();
for(ll i = 1;i <= n;i++){
for(ll j = i+1;j <= n;j++){
if(f[j] == f[i]+1 && a[i] <= a[j]){
add(i+n,j,1);
add(j,i+n,0);
}
}
}
s = 0,t = 2*n+1;
for(ll i = 1;i <= n;i++){
if(f[i] == 1){
add(s,i,1);
add(i,s,0);
}
add(i,i+n,1);
add(i+n,i,0);
if(f[i] == len){
add(i+n,t,1);
add(t,i+n,0);
}
}
cout<<dinic()<<endl;
memset(head,0,sizeof head);
cnt = 1;
for(ll i = 1;i <= n;i++){
for(ll j = i+1;j <= n;j++){
if(f[j] == f[i]+1 && a[i] <= a[j]){
add(i+n,j,1);
add(j,i+n,0);
}
}
}
s = 0,t = 2*n+1;
for(ll i = 1;i <= n;i++){
if(f[i] == 1){
add(s,i,1);
add(i,s,0);
}
add(i,i+n,1);
add(i+n,i,0);
if(f[i] == len){
add(i+n,t,1);
add(t,i+n,0);
}
}
if(f[1] == 1)add(s,1,n);add(1,s,0);
add(1,1+n,n);add(1+n,1,0);
add(n,2*n,n);add(2*n,n,0);
if(f[n] == len)add(2*n,t,n);add(t,2*n,0);
cout<<dinic()<<endl;
return 0;
}