抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

题意:

给定序列 A,序列中的每一项 AiA_i 有删除代价 BiB_i 和附加属性 CiC_i。请删除若干项,使得 A 的最长上升子序列长度减少至少 1,且付出的代价之和最小,并输出方案。如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。

n700n \leq 700

题解:

列出 LIS 的转移方程:

f[i]=maxj<i,aj<aif[j]+1f[i] = \max_{j < i, a_j < a_i} f[j] + 1

后就会发现 :

f[i]f[i] 的取值由所有满足 j<i,aj<ai,f[j]+1=f[i]j < i, a_j < a_i, f[j] + 1 = f[i] 的 j 制约着。

考虑建图:先拆点,并在中间连 bib_i 的边 , 将满足所有上述条件的 (i,j)(i, j) 连一条正无穷的边。

容易观察,答案为这张图的最小割。

接着考虑输出方案。

首先考虑这样一个事实:一条边能够出现在最小割边集中,当且仅当它在所有最大流方案中均满流。(证明可以考虑将这条边容量减小 1,最大流的变化)

实际验证时,只需要检查这条边所连接两点在残量网络中是否连通即可,(如果连通,则可以将这条边减少流量)

这时我们就可以按 cc 值从小到大考虑每一条边 (u,v)(u, v)。若能够在最小割中,就将其加入答案,并删除这条边。(当然将 su,vts \rightarrow u, v \rightarrow t 的流量退回)。

( 话说这道题卡常,谁会网络流卡常的? )

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
int n;
int a[MAXN], b[MAXN], c[MAXN];
namespace solver1 {
const int MAXM = 3e6;
struct Edge {
int v, cap, next;
}edge[MAXM];
int head[MAXN << 1], tail, s, t;
int f[MAXN], p[MAXN], seq[MAXN], asq[MAXN];
inline int insert(int u, int v, int cap) {
edge[++tail] = (Edge) {v, cap, head[u]}; head[u] = tail;
edge[++tail] = (Edge) {u, 0, head[v]}; head[v] = tail;
return tail - 1;
}
inline bool cmp(int x, int y) {return c[x] < c[y];}

namespace maxflow {
int cur[MAXN << 1];
int dis[MAXN << 1], t;
bool bfs(int s, int t) {
std::queue <int> q;
q.push(s);
memset(dis, 63, sizeof dis);
dis[s] = 0;
while(!q.empty()) {
int u = q.front(); q.pop();
for (register int i = head[u]; i; i = edge[i].next) {
register int v = edge[i].v;
if (edge[i].cap && dis[v] == INF) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
return dis[t] != INF;
}
int dfs(int u, int a) {
if (u == t || a == 0) return a;
int ans = 0;
for (int &i = cur[u]; i; i = edge[i].next) {
int v = edge[i].v, f;
if (dis[u] + 1 == dis[v] && (f = dfs(v, std::min(a, edge[i].cap))) > 0) {
edge[i].cap -= f;
edge[i ^ 1].cap += f;
a -= f;
ans += f;
}
}
return ans;
}
long long main(int ss, int tt) {
t = tt;
long long ans = 0;
while(bfs(ss, tt)) {
memcpy(cur, head, sizeof cur);
ans += dfs(ss, INF);
}
return ans;
}
}
void main() {
memset(head, 0, sizeof head); tail = 1;

s = 2 * n + 1, t = 2 * n + 2;
int maxf = 0;
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) {
f[i] = std::max(f[j] + 1, f[i]);
}
}
maxf = std::max(maxf, f[i]);
}

for (int i = 1; i <= n; i++) {
p[i] = insert(i * 2 - 1, i * 2, b[i]);
}

for (int i = 1; i <= n; i++) {
if (f[i] == 1) {
insert(s, i * 2 - 1, INF);
} else {
for (int j = 1; j < i; j++) {
if (a[j] < a[i] && f[j] + 1 == f[i]) {
insert(j * 2, i * 2 - 1, INF);
}
}
}
if (f[i] == maxf) {
insert(i * 2, t, INF);
}
}

long long ans = maxflow::main(s, t);
printf("%lld ", ans);


for (int i = 1; i <= n; i++) seq[i] = i;
std::sort(seq + 1, seq + n + 1, cmp);

int cnt = 0;
for (int x = 1; x <= n; x++) {
int i = seq[x];
if (!edge[p[i]].cap &&
!maxflow::bfs(i * 2 - 1, i * 2)) {
maxflow::main(i * 2 - 1, s);
maxflow::main(t, i * 2);
asq[++cnt] = i;
edge[p[i]].cap = 0;
edge[p[i] ^ 1].cap = 0;
}
}
std::sort(asq + 1, asq + cnt + 1);
printf("%d\n", cnt);
for (int i = 1; i <= cnt; i++) {
printf("%d%c", asq[i], " \n"[i == cnt]);
}

}
}