• [BZOJ 3218] A+B Problem


    Link:

    BZOJ 3218 传送门

    Solution:

    由于染色将点集分为两块,想到最小割模型

    最大化权值可以看成总和减去最小化损失,于是由“最大割” ----> 最小割

    (1)网络流建图

    $<S,i,b[i]>$割掉表示选白色,$<i′,T,w[i]>$割掉表示选黑色,

    接下来对于“奇怪的点对”$(i,j)$,连接$<i,j,p[i]>$,表示如果同时保留$<S,i,b[i]>$和$<j,T,w[j]>$就要割掉$<i,j,p[i]>$

    但这样仍存在问题:对于不同的$j$,$p[i]$会计算多次

    于是我们新增节点$i''$,$<i,i′',p[i]>$保证$p[i]$只计算1次,$<i′',j,INF>$表示和$j$是不会被割的

    这样直接求出$mincut$,然后答案就是$sum {(b[i]+w[i])}−mincut$

    (2)数据结构优化边数

    但这样的边数是$O(n^2)$,$MLE+TLE$双喜临门

    注意影响的范围是一段区间,所以可以用线段树的节点去表示对一个区间的影响,来达到优化的目的

    即由连接$<i′',j,INF>$改为连$<i′',seg[l,r],INF>$

    此外还要连接线段树中父子关系:$<fa,ch[0/1],INF>$,以及叶子节点及其对应的点:$<leaf_i,i,INF>$。

    由于 V(du)F(liu)K 要求$1<=j<i$,强行可持久化,于是将线段树要改为可持久化线段树

    这样边数和点数都是$n*log(n)$的,就可以通过了。

    Note:

    这里有一个问题就是$a[i]$可能多次出现,于是要保证连接一个$a[i]$后便等效于连接了所有$a[i]$

    所以需要新版本的$a[i]$叶子连向老版本的$a[i]$叶子$<new,old,INF>$

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define mid (l+r)/2
    const int MAXN=1e5+10;
    const int INF=1<<27;
    struct testdata{int a,b,w,l,r,p;}dat[MAXN];
    int n,S,T,cnt=0,dsp[MAXN],root[MAXN],tot=0,res=0;
    
    namespace MaxFlow //最大流
    {
        int level[MAXN],iter[MAXN];
        struct edge{int to,cap,rev;};
        vector<edge> G[MAXN];
        
        inline void add_edge(int from,int to,int cap)
        {
            G[from].push_back(edge{to,cap,G[to].size()});
            G[to].push_back(edge{from,0,G[from].size()-1});
        }
        bool bfs()
        {
            memset(level,-1,sizeof(level));
            queue<int> que;que.push(S);level[S]=0;
            while(!que.empty())
            {
                int u=que.front();que.pop();
                for(int i=0;i<G[u].size();i++)
                {
                    edge &e=G[u][i];
                    if(e.cap && level[e.to]==-1)
                        level[e.to]=level[u]+1,que.push(e.to);
                }
            }
            return (level[T]>0);
        }
        int dfs(int v,int f)
        {
            if(v==T) return f;
            for(int &i=iter[v];i<G[v].size();i++)
            {
                edge &e=G[v][i];
                if(e.cap && level[e.to]==level[v]+1)
                {
                    int d=dfs(e.to,min(e.cap,f));
                    if(d)
                    {
                        e.cap-=d;
                        G[e.to][e.rev].cap+=d;
                        return d;
                    }
                }
            }
            return 0;
        }
        int dinic()
        {
            int ret=0;
            while(bfs())
            {
                memset(iter,0,sizeof(iter));int f;
                while((f=dfs(S,INF))>0) ret+=f;
            }
            return ret;
        }
    };
    
    namespace PrTree //主席树
    {
        int ch[MAXN][2];
        void Insert(int &x,int y,int pos,int id,int l,int r)
        {
            x=++cnt;ch[x][0]=ch[y][0];ch[x][1]=ch[y][1];
            if(l==r)
            {
                MaxFlow::add_edge(2*n+x,id,INF);
                if(y) MaxFlow::add_edge(2*n+x,2*n+y,INF); //对相同点的特殊处理
                return;
            }
            if(pos<=mid) Insert(ch[x][0],ch[y][0],pos,id,l,mid);
            else Insert(ch[x][1],ch[y][1],pos,id,mid+1,r);
            if(ch[x][0]) MaxFlow::add_edge(2*n+x,2*n+ch[x][0],INF);
            if(ch[x][1]) MaxFlow::add_edge(2*n+x,2*n+ch[x][1],INF);
        }
        void Query(int cur,int a,int b,int id,int l,int r)
        {
            if(!cur) return;
            if(a<=l && r<=b)
            {
                MaxFlow::add_edge(id,2*n+cur,INF);
                return;
            }
            if(a<=mid) Query(ch[cur][0],a,b,id,l,mid);
            if(b>mid) Query(ch[cur][1],a,b,id,mid+1,r);
        }
    };
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d%d%d%d",&dat[i].a,&dat[i].b,&dat[i].w,&dat[i].l,&dat[i].r,&dat[i].p),
            res+=(dat[i].b+dat[i].w);
        
        for(int i=1;i<=n;i++) //离散化
            dsp[++tot]=dat[i].a,dsp[++tot]=dat[i].l,dsp[++tot]=dat[i].r;
        sort(dsp+1,dsp+tot+1);tot=unique(dsp+1,dsp+tot+1)-dsp-1;
        for(int i=1;i<=n;i++)
            dat[i].a=lower_bound(dsp+1,dsp+tot+1,dat[i].a)-dsp,
            dat[i].l=lower_bound(dsp+1,dsp+tot+1,dat[i].l)-dsp,
            dat[i].r=lower_bound(dsp+1,dsp+tot+1,dat[i].r)-dsp;
        
        S=0;
        for(int i=1;i<=n;i++)
            PrTree::Insert(root[i],root[i-1],dat[i].a,i,1,tot),
            PrTree::Query(root[i-1],dat[i].l,dat[i].r,i+n,1,tot);
        T=2*n+cnt+1;
        
        for(int i=1;i<=n;i++) //建图
            MaxFlow::add_edge(S,i,dat[i].b),
            MaxFlow::add_edge(i,T,dat[i].w),
            MaxFlow::add_edge(i,i+n,dat[i].p);
        printf("%d",res-MaxFlow::dinic());
        return 0;
    }

    Review:

     很吼的一道题目啊

    (1)对点集分类,想到最小割

    同时如果求“最大割”,由最小代价来转化

    (2)如果对于多个二元组只计算一次权值时,拆点!

    连边$<i,i'',cost_i>$来保证只计算一次权值,而$<i′',j,INF>$保证不会计入答案

    (3)数据结构“区间建图”

    第一次见到的操作(orz VFK),如果连边时是区间操作,考虑使用连接RMQ数据结构上节点的方式来优化边数

    (4)如果要保证$1<=j<i$,记得可持久化

    其实就是记录前缀

    (5)如果数据本身不重要,只考虑大小关系时,使用离散化简化数据集合

  • 相关阅读:
    [原]写项目方案书的六字方针
    [原]简述Field,Attribute,Property的区别
    [原]Excel(电子表格)中的大写变成小写
    [原]滚动的图片或文字(有缝)
    [原]按字母索引(asp版)
    [原]sql语句上一篇,下一篇
    [原]选中行后高亮显示
    自定义报表开发、使用手册
    男人必须终身牢记的一句话
    在sqlplus中操作blob和clob
  • 原文地址:https://www.cnblogs.com/newera/p/9130659.html
Copyright © 2020-2023  润新知