[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)如果数据本身不重要,只考虑大小关系时,使用离散化简化数据集合

 

posted @ 2018-06-03 21:42  NewErA  阅读(168)  评论(0编辑  收藏  举报