[BZOJ3532][Sdoi2014]Lis(网络最小割)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=3532

Solution

先求 f[i] f [ i ] 表示以 i i 为结尾的最长上升子序列长度, L 为整个序列的最长上升子序列长度。
(1)由源点 S S 向所有满足 f[i]=1 的点 i i 连容量为 的边。
(2)由所有满足 f[i]=L f [ i ] = L 的点 i i 向汇点 T 连容量为 的边。
(3)对于一对 i<j i < j ,如果 f[i]+1=f[j] f [ i ] + 1 = f [ j ] 并且 Ai<Aj A i < A j ,就连边 <i,j> < i , j > <script type="math/tex" id="MathJax-Element-8781"> </script> ,容量
问题转化成删掉一些点使得 S S T 没有路径。
将每个点拆成两个( i i i ),原先连向该点的边都连向 i i ,原先由该点连出的边都由 i 连出,从 i i i 连容量 Bi B i 的边。
于是删点转化成割边。求最小割(最大流)即可得到第一问的答案。
难点在第二问。字典序显然可以贪心。
从小到大枚举 Ci C i ,如果边 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8793"> </script> 可以出现在最小割中就选 i i ,否则考虑下一个。
回顾 AHOI 2009 Mincut 最小割……
<i,i> 可能出现在最小割中,当且仅当删掉边 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8796"> </script> 之后最小割减小的容量为 Bi B i
证明:如果删掉边 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8798"> </script> 之后最小割容量减少的值不足 Bi B i ,那么这个割的方案再加上边 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8800"> </script> 之后的容量一定超过了原来的最小割,这时候 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8801"> </script> 不能出现在最小割中,否则最小割容量减小了 Bi B i ,则加上了 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8803"> </script> 后恰好是最小割,故可以出现在最小割中。
删掉边后重新跑一遍最大流,会 TLE 。
等价地,如果残量网络中有从 i i i 的路径,那么即使 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8806"> </script> 边被删掉, i i 仍然可以通过 i i i ′ 的路径传输至少 1 1 的流量到 i ,这样最小割减少的量就不是 Bi B i 了。
故边 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8813"> </script> 能出现在最小割中当且仅当残量网络中没有 i i i 的路径。
每次选择了 Ci C i 之后,我们需要将边 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8817"> </script> 删掉重新跑最大流,还是 TLE 。
但我们考虑到:删掉 <i,i> < i , i ′ > <script type="math/tex" id="MathJax-Element-8818"> </script> 只会影响 S S i 的增广路集合和 i i ′ T T 的增广路集合。
考虑退流:从 T i i ′ 跑一遍最大流,再由 i i S 跑一遍最大流。看上去跑了两次,但是流过的边集变小了,故复杂度可以接受。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define NF(u) for (int &e = cur[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 705, M = 1414, L = 1e6 + 5;
const ll INF = 1ll << 60;
int n, a[N], b[N], ecnt, nxt[L], adj[M], st[L], cur[M], go[L],
lev[M], len, que[M], f[N], vis[M], dalao, cnt, ans[N];
ll cap[L];
struct cyx {
    int id, x;
} c[N];
bool comp(cyx a, cyx b) {
    return a.x < b.x;
}
void add_edge(int u, int v, ll w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; st[ecnt] = u;
    go[ecnt] = v; cap[ecnt] = w;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; st[ecnt] = v;
    go[ecnt] = u; cap[ecnt] = 0;
}
bool bfs(int S, int T) {
    int i;
    For (i, 1, (n << 1) + 2) lev[i] = -1, cur[i] = adj[i];
    lev[que[len = 1] = S] = 0;
    For (i, 1, len) {
        int u = que[i];
        Edge(u) if (cap[e] && lev[v] == -1) {
            lev[que[++len] = v] = lev[u] + 1;
            if (v == T) return 1;
        }
    }
    return 0;
}
ll dinic(int T, int u, ll flow) {
    if (u == T) return flow;
    ll res = 0, delta;
    NF(u) if (cap[e] && lev[u] < lev[v]) {
        delta = dinic(T, v, min(cap[e], flow - res));
        if (delta) {
            cap[e] -= delta; cap[e ^ 1] += delta;
            res += delta; if (res == flow) break;
        }
    }
    if (res != flow) lev[u] = -1;
    return res;
}
ll maxflow(int S, int T) {
    ll res = 0;
    while (bfs(S, T)) res += dinic(T, S, INF);
    return res;
}
int which(int x, int y) {
    return 2 + (x - 1 << 1) + y;
}
bool check(int S, int T) {
    int i;
    vis[que[len = 1] = S] = ++dalao;
    For (i, 1, len) {
        int u = que[i];
        Edge(u) {
            if (!cap[e] || vis[v] == dalao) continue;
            que[++len] = v;
            vis[v] = dalao;
            if (v == T) return 1;
        }
    }
    return 0;
}
void work() {
    int i, j, lis = cnt = 0;
    n = read();
    For (i, 1, n) a[i] = read();
    For (i, 1, n) b[i] = read();
    For (i, 1, n) c[c[i].id = i].x = read();
    sort(c + 1, c + n + 1, comp);
    ecnt = 1;
    For (i, 1, (n << 1) + 2) adj[i] = 0;
    For (i, 1, n) {
        f[i] = 1;
        For (j, 1, i - 1) if (a[j] < a[i])
            f[i] = max(f[i], f[j] + 1);
        lis = max(lis, f[i]);
    }
    For (i, 1, n) add_edge(which(i, 0), which(i, 1), b[i]);
    For (i, 1, n) {
        if (f[i] == 1) add_edge(1, which(i, 0), INF);
        if (f[i] == lis) add_edge(which(i, 1), (n << 1) + 2, INF);
    }
    For (i, 1, n) For (j, i + 1, n)
        if (a[i] < a[j] && f[i] + 1 == f[j])
            add_edge(which(i, 1), which(j, 0), INF);
    ll sum = maxflow(1, (n << 1) + 2);
    For (i, 1, n) {
        int e = c[i].id << 1, u = st[e], v = go[e];
        if (check(u, v)) continue;
        ans[++cnt] = c[i].id;
        maxflow((n << 1) + 2, v); maxflow(u, 1);
    }
    printf("%lld %d\n", sum, cnt);
    sort(ans + 1, ans + cnt + 1);
    For (i, 1, cnt) printf("%d ", ans[i]);
    printf("\n");
}
int main() {
    int T = read();
    while (T--) work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值