[bzoj3532][Sdoi2014]Lis【最小割】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=3532
【题解】
  第一问比较简单,先求出到每个点为止的最长上升子序列,记作 f f ,若a[i]>a[j] i>j i > j fi=fj+1 f i = f j + 1 那么从 i i j连一条边权为 inf i n f 的边,每个点拆成入点和出点,之间连一条 bi b i 的边表示代价, S S fi==1的点连边, fi==max f i == m a x 的点向 T T 连边。跑出的最小割即为答案。
  第二问,我们从小到大枚举每个c,若一个点可以在割集中,那么入点是永远走不到出点的。因为所有可能是最小割的边一定是全部流满的。若可行,接下来将这条边在网络流中删去并加入割集,删去的方法是 :
   设这条边为 (u,v,l) ( u , v , l )
   从 u u S l l 的流量,再从T v v l的流量,最后将原来的边去除。
  由于这条边在一个割集中,所以退流后一定无法继续增广,所以一定是合法的。
  时间复杂度: 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值