zoukankan      html  css  js  c++  java
  • [BZOJ 3218] A + B Problem 【可持久化线段树 + 网络流】

    题目连接:BZOJ - 3218

    题目分析

    题目要求将 n 个点染成黑色或白色,那么我们可以转化为一个最小割模型。

    我们规定一个点 i 最后属于 S 集表示染成黑色,属于 T 集表示染成白色,那么对于每个点 i 就要连边 (S, i, B[i]) 和 (i, T, W[i])。

    这样,如果一个点属于 S 集,就要割掉与 T 相连的边,就相当于失去了染成白色的收益。

    我们再来考虑 “奇怪的点”,一个点 i 变成奇怪的点的条件是:i 是黑色且存在一个白色点 j 满足 j < i && L[i] <= A[j] <= R[i]。

    对于这一点,我们可以对于每个点 i 再添加一个点 n + i ,连边 (i, n + i, P[i]) ,对于每个符合条件的 j ,连边 (n + i, j, INF) 。

    这样,就实现了,只要有 j 是白色,那么 j 与 T 相连,n + i 就一定与 T 相连,而如果 i 是与 S 相连,就一定要割掉 (i -> n + i) 的价值为 P[i] 的边。

    然而这样的边数可以达到 n^2 级别,是不能通过全部数据的。

    我们可以考虑用线段树优化网络流的连边。

    我们建一棵权值线段树(对所有的 L, R, A 离散化后会节省时间和空间),从每个 j 对应的 A[j] 节点向 j 连 INF 边,从线段树节点向这个节点的儿子节点连 INF 边,对于每个 i ,就再线段树中找到对应的 [L[i], R[i]] 区间,从 n + i 向组成这些区间的节点连 INF 边。 

    然而我们还没有考虑 j < i 的限制,那么使用可持久化线段树就可以了。

    需要注意的一点是,对于第 i 个位置,插入 A[i] 时,是依托 i - 1 的线段树版本建立的,如果发现在 i - 1 版本的线段树中已经存在权值为 A[i] 的节点,那么就从 i 版本表示 A[i] 单点的线段树节点(设为Now)向 i - 1 版本的线段树中表示 A[i] 单点的线段树节点 (设为 Last)连边 (Now, Last, INF) 。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <map>
     
    using namespace std;
     
    const int MaxN = 5000 + 5, MaxNode = 100000 + 5, MaxM = 200000 + 5, INF = 999999999;
     
    map<int, int> M;
     
    int n, Top, Tot, S, T, HN, Index, Ans;
    int A[MaxN], B[MaxN], W[MaxN], L[MaxN], R[MaxN], Pi[MaxN], Arr[MaxN * 3];
    int Root[MaxN], Son[MaxNode][2], d[MaxNode], Num[MaxNode];
     
    struct Edge
    {
        int u, v, w;
        Edge *Next, *Other;
    } E[MaxM * 2], *P = E, *Point[MaxNode], *Last[MaxNode];
     
    inline void AddEdge(int x, int y, int z)
    {
        Edge *Q = ++P; ++P;
        P -> u = x; P -> v = y; P -> w = z; 
        P -> Next = Point[x]; Point[x] = P; P -> Other = Q;
        Q -> u = y; Q -> v = x; Q -> w = 0;
        Q -> Next = Point[y]; Point[y] = Q; Q -> Other = P;
    }
     
    void Insert(int &x, int Lt, int s, int t, int Pos, int Idx)
    {
        if (x == 0) x = ++Index;
        if (s == t) 
        {
            AddEdge(Tot + x, Idx, INF);
            if (Lt) AddEdge(Tot + x, Tot + Lt, INF);
            return;
        }
        int m = (s + t) >> 1;
        if (Pos <= m) 
        {
            Son[x][1] = Son[Lt][1];
            Insert(Son[x][0], Son[Lt][0], s, m, Pos, Idx);
        }
        else
        {
            Son[x][0] = Son[Lt][0];
            Insert(Son[x][1], Son[Lt][1], m + 1, t, Pos, Idx);
        }
        if (Son[x][0]) AddEdge(Tot + x, Tot + Son[x][0], INF);
        if (Son[x][1]) AddEdge(Tot + x, Tot + Son[x][1], INF);
    }
     
    void Link(int x, int s, int t, int l, int r, int Idx)
    {
        if (l <= s && r >= t)
        {
            AddEdge(n + Idx, Tot + x, INF);
            return; 
        }
        int m = (s + t) >> 1;
        if (Son[x][0] && l <= m) Link(Son[x][0], s, m, l, r, Idx);
        if (Son[x][1] && r >= m + 1) Link(Son[x][1], m + 1, t, l, r, Idx);
    }
     
    inline int gmin(int a, int b) {return a < b ? a : b;}
     
    int DFS(int Now, int Flow)
    {
        if (Now == T) return Flow;
        int ret = 0;
        for (Edge *j = Last[Now]; j; j = j -> Next)
        {
            if (j -> w && d[Now] == d[j -> v] + 1)
            {
                int p = DFS(j -> v, gmin(j -> w, Flow - ret));
                j -> w -= p; j -> Other -> w += p; ret += p;
                if (ret == Flow) return ret;
            }
        }   
        if (d[S] >= Tot) return ret;
        if (--Num[d[Now]] == 0) d[S] = Tot;
        ++Num[++d[Now]];
        Last[Now] = Point[Now];
        return ret;
    }
     
    int main()
    {
        scanf("%d", &n);
        Top = 0;
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d%d%d%d%d%d", &A[i], &B[i], &W[i], &L[i], &R[i], &Pi[i]);
            Arr[++Top] = A[i]; Arr[++Top] = L[i]; Arr[++Top] = R[i];
        }
        sort(Arr + 1, Arr + Top + 1);
        HN = 0;
        M.clear();
        for (int i = 1; i <= Top; ++i)
        {
            if (i != 1 && Arr[i] == Arr[i - 1]) continue;
            M[Arr[i]] = ++HN;
        }
        for (int i = 1; i <= n; ++i)
        {
            A[i] = M[A[i]];
            L[i] = M[L[i]]; R[i] = M[R[i]];
        }
        memset(Root, 0, sizeof(Root));
        S = n * 2 + 1; T = n * 2 + 2;
        Tot = n * 2 + 2;
        Index = 0;
        Ans = 0;
        for (int i = 1; i <= n; ++i)
        {   
            AddEdge(S, i, B[i]);
            AddEdge(i, T, W[i]);
            AddEdge(i, n + i, Pi[i]);
            Ans += B[i] + W[i];
        }
        for (int i = 1; i <= n; ++i)
        {
            if (i > 1) Link(Root[i - 1], 1, HN, L[i], R[i], i);
            Insert(Root[i], Root[i - 1], 1, HN, A[i], i);
        }
        Tot = Tot + Index;
        memset(d, 0, sizeof(d));
        memset(Num, 0, sizeof(Num)); Num[0] = Tot;
        for (int i = 1; i <= Tot; ++i) Last[i] = Point[i];
        while (d[S] < Tot) Ans -= DFS(S, INF);
        printf("%d
    ", Ans);
        return 0;
    }
  • 相关阅读:
    JAVA文件操作类和文件夹的操作代码示例
    java去除表达符号的正则表达式
    正则表达式以过滤特殊字符
    eclipse与myeclipse恢复已删除的文件和代码
    Windows 2003 Server R2 x64 IIS6.0 eWebEditor无法显示的问题
    获得每日,每周,每月的0点和24点的时间戳
    Access查询时间段 .
    java连接Access数据库的两种方法
    移动App专项测试
    linux性能评估-内存基础理解篇
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4380193.html
Copyright © 2011-2022 走看看