【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=3532
【题解】
第一问比较简单,先求出到每个点为止的最长上升子序列,记作
f
f
,若且
i>j
i
>
j
且
fi=fj+1
f
i
=
f
j
+
1
那么从
i
i
向连一条边权为
inf
i
n
f
的边,每个点拆成入点和出点,之间连一条
bi
b
i
的边表示代价,
S
S
向的点连边,
fi==max
f
i
==
m
a
x
的点向
T
T
连边。跑出的最小割即为答案。
第二问,我们从小到大枚举每个,若一个点可以在割集中,那么入点是永远走不到出点的。因为所有可能是最小割的边一定是全部流满的。若可行,接下来将这条边在网络流中删去并加入割集,删去的方法是 :
设这条边为
(u,v,l)
(
u
,
v
,
l
)
从
u
u
向流
l
l
的流量,再从向
v
v
流的流量,最后将原来的边去除。
由于这条边在一个割集中,所以退流后一定无法继续增广,所以一定是合法的。
时间复杂度:
O(N3)
O
(
N
3
)
并不是很满。
【代码】
# include <bits/stdc++.h>
# define ll int
# define inf 0x3f3f3f3f
# define N 1510
# define M 2000100
using namespace std;
int read(){
int tmp = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9'){tmp = tmp * 10 + ch - '0'; ch = getchar(); }
return tmp * fh;
}
int pin[N], pout[N];
ll ans, a[N], b[N], c[N], h[N];
struct Edge{
int data, next, re;
ll l;
}e[M];
int m, head[N], S, T, cur[N], dis[N], q[N], place, use[N], n, f[N], ansnum, sum[N], ex, belong[N], p[N];
void build(int u, int v, ll l){
e[++place].data = v; e[place].next = head[u]; head[u] = place; e[place].l = l; e[place].re = place + 1;
e[++place].data = u; e[place].next = head[v]; head[v] = place; e[place].l = 0; e[place].re = place - 1;
}
void bfs(int S, int T){
memset(dis, inf, sizeof(dis));
dis[S] = 0; int pl = 1, pr = 1; q[1] = S;
while (pl <= pr){
int x = q[pl++];
for (int ed = head[x]; ed != 0; ed = e[ed].next)
if (dis[e[ed].data]==inf && e[ed].l > 0){
dis[e[ed].data] = dis[x] + 1,
q[++pr] = e[ed].data;
if (q[pr] == T) return;
}
}
}
int dfs(int x, ll l, int T){
if (x == T) return l;
ll sum = 0;
for (int ed = cur[x]; ed != 0; ed = e[ed].next)
if (dis[e[ed].data] == dis[x] + 1 && e[ed].l > 0){
ll f = dfs(e[ed].data, min(e[ed].l, l), T);
sum = sum + f, l = l - f;
e[ed].l -= f; e[e[ed].re].l += f;
if (l == 0){
cur[x] = ed;
return sum;
}
}
cur[x] = 0;
return sum;
}
ll dinic(int S, int T, ll l){
ll sum = 0;
for (bfs(S, T); dis[T] != inf && l; bfs(S, T)){
for (int i = 1; i <= ex; i++) cur[i] = head[i];
ll tmp = dfs(S, l, T);
sum += tmp;
l -= tmp;
}
return sum;
}
void upflow(int u, int v, int l){
dinic(u, S, l);
dinic(T, v, l);
}
bool cmp(int x, int y){
return c[x] < c[y];
}
int main(){
// freopen("H7.in", "r", stdin);
// freopen("H7.out", "w", stdout);
for (int opt = read(); opt--;){
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++) b[i] = read();
for (int i = 1; i <= n; i++) c[i] = read();
int num = 0;
for (int i = 1; i <= n; i++){
int pl = 1, pr = num, now = 0;
while (pl <= pr){
int mid = (pl + pr) / 2;
if (h[mid] < a[i])
now = mid, pl = mid + 1;
else pr = mid - 1;
}
f[i] = now + 1;
h[now + 1] = a[i];
num = max(num, now + 1);
}
S = 2 * n + 1, T = 2 * n + 2, ex = 2 * n + 3;
memset(head, 0, sizeof(head)); place = 0;
for (int i = 1; i <= n; i++)
pin[i] = i * 2 - 1,
pout[i] = i * 2;
for (int i = 1; i <= n; i++){
build(pin[i], pout[i], b[i]);
belong[i] = place - 1;
}
for (int i = 1; i <= n; i++){
if (f[i] == 1)
build(S, pin[i], inf);
else {
for (int j = 1; j < i; j++)
if (f[j] + 1 == f[i] && a[j] < a[i])
build(pout[j], pin[i], inf);
}
if (f[i] == num)
build(pout[i], T, inf);
}
ans = dinic(S, T, inf);
ansnum = 0;
for (int i = 1; i <= n; i++) p[i] = i;
sort(p + 1, p + n + 1, cmp);
for (int i = 1; i <= n; i++){
int x = p[i];
bfs(pin[x], pout[x]);
if (dis[pout[x]] == inf){
sum[++ansnum] = x;
upflow(pin[x], pout[x], b[x]);
e[belong[x]].l = e[belong[x] + 1].l = 0;
}
}
sort(sum + 1, sum + ansnum + 1);
printf("%d %d\n", ans, ansnum);
for (int i = 1; i <= ansnum; i++)
printf("%d%c", sum[i], (i == ansnum) ? '\n' : ' ');
}
return 0;
}