题目描述
给定正整数序列 x_1 \ldots, x_nx1…,xn。
- 计算其最长不下降子序列的长度 ss。
- 如果每个元素只允许使用一次,计算从给定的序列中最多可取出多少个长度为 ss 的不下降子序列。
- 如果允许在取出的序列中多次使用 x_1x1 和 x_nxn(其他元素仍然只允许使用一次),则从给定序列中最多可取出多少个不同的长度为 ss 的不下降子序列。
令 a_1, a_2, \ldots, a_sa1,a2,…,as 为构造 SS 时所使用的下标,b_1, b_2, \ldots, b_sb1,b2,…,bs 为构造 TT 时所使用的下标。且 \forall i \in [1,s-1]∀i∈[1,s−1],都有 a_i < a_{i+1}ai<ai+1,b_i < b_{i+1}bi<bi+1。则 SS 和 TT 不同,当且仅当 \exists i \in [1,s]∃i∈[1,s],使得 a_i \neq b_iai=bi。
输入格式
第一行有一个正整数 n,表示给定序列的长度。接下来的一行有 n 个正整数x1,...,xn。
输出格式
- 第 1 行是最长不下降子序列的长度 s。
- 第 2 行是可取出的长度为 s 的不下降子序列个数。
- 第 3 行是允许在取出的序列中多次使用 x1 和xn 时可取出的长度为 ss 的不同的不下降子序列个数。
输入输出样例
输入 #1复制
4 3 6 2 5
输出 #1复制
2 2 3
说明/提示
1≤n≤500
思路:
1、第一问直接LIS,求得的最长不下降子序列的长度为ss
2、第二问求可取出的长度为 s 的不下降子序列个数,每个数最多使用1次,考虑拆点,1~n表示入点,n + 1~2 * n表示出点,0表示超级源点,2 * n + 1表示超级汇点,dinic跑网络流,如下建图:
(1)所有入点向出点连边,流量为1
(2)超级源点向dp[i] == 1的入点连边,流量为1
(3)dp[i] == ss 的出点向超级汇点连边,流量为1
3、第三问只增加了一个条件,第一个数和最后一个数可以使用多次,它只影响了图中超级源点到1、1的入点到1的出点、n的入点到n的出点、n的出点到超级汇点(在dp[n] == ss的前提下),所以在第二问建图的基础上再添加几条边就可以了
(有关板子问题:第三问如果不重新构图,即直接在原图上加边,再跑出的maxflow是新增的流量,要和第二问的maxflow加起来
当然也可以init一遍重新构图,毕竟n就500,但这样显得很憨)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1005;
const int M = 10005; //边集二倍
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int head[N], tot, n, m, x, s, t;
int Q[N];
int dep[N], cur[N], sta[N]; ///数组cur记录点u之前循环到了哪一条边
struct Edge {
int to, next, cap, flow;
}edge[M];
void init() {
tot = 2;
memset(head, -1, sizeof(head));
memset(edge, 0, sizeof(edge));
}
void addedge(int u, int v, int w, int rw = 0) {
edge[tot].to = v;
edge[tot].cap = w;
edge[tot].flow = 0;
edge[tot].next = head[u];
head[u] = tot++;
edge[tot].to = u;
edge[tot].cap = rw;
edge[tot].flow = 0;
edge[tot].next = head[v];
head[v] = tot++;
}
bool bfs(int s, int t, int n) {
int fron = 0, tail = 0;
memset(dep, -1, sizeof(dep[0]) * (n + 1));
dep[s] = 0;
Q[tail++] = s;
while(fron < tail) {
int u = Q[fron++];
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dep[v] == -1) {
dep[v] = dep[u] + 1;
if(v == t) return true;
Q[tail++] = v;
}
}
}
return false;
}
int dinic(int s, int t, int n) {
int maxflow = 0;
while(bfs(s, t, n)) {
for(int i = 0; i <= n; ++i) cur[i] = head[i];
int u = s, tail = 0;
while(cur[s] != -1) {
if(u == t) {
int tp = inf;
for(int i = tail - 1; i >= 0; --i)
tp = min(tp, edge[sta[i]].cap - edge[sta[i]].flow);
maxflow += tp;
for(int i = tail - 1; i >= 0; --i) {
edge[sta[i]].flow += tp;
edge[sta[i] ^ 1].flow -= tp;
if(edge[sta[i]].cap - edge[sta[i]].flow == 0)
tail = i;
}
u = edge[sta[tail] ^ 1].to;
}
else if(cur[u] != -1 && edge[cur[u]].cap > edge[cur[u]].flow && dep[u] + 1 == dep[edge[cur[u]].to]) {
sta[tail++] = cur[u];
u = edge[cur[u]].to;
}
else {
while(u != s && cur[u] == -1)
u = edge[sta[--tail] ^ 1].to;
cur[u] = edge[cur[u]].next;
}
}
}
return maxflow;
}
int a[N], dp[N];
int main() {
scanf("%d", &n);
s = 0, t = 2 * n + 1;
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
if(n == 1) {
printf("1\n1\n1\n");
return 0;
}
int ss = 0;
for(int i = 1; i <= n; ++i) dp[i] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j < i; ++j) {
if(a[j] <= a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
ss = max(ss, dp[i]);
}
printf("%d\n", ss);
init();
for(int i = 1; i <= n; ++i) {
addedge(i, i + n, 1);
if(dp[i] == 1) addedge(s, i, 1);
if(dp[i] == ss) addedge(i + n, t, 1);
for(int j = 1; j < i; ++j) {
if(dp[j] + 1 == dp[i] && a[j] <= a[i])
addedge(j + n, i, 1);
}
}
int maxflow = dinic(s, t, t + 1);
printf("%d\n", maxflow);
addedge(1, n + 1, inf);
addedge(n, 2 * n, inf);
addedge(s, 1, inf);
if(dp[n] == ss) addedge(2 * n, t, inf);
maxflow += dinic(s, t, t + 1); ///maxflow +=
printf("%d\n", maxflow);
return 0;
}