【线段树】【树】【网络流】[UOJ#77]A+B Problem

题目描述

这里写图片描述
这里写图片描述
感谢geng给我了正确的数据范围

题目解析

首先我们可以发现如果我们不考虑有奇怪的格子,那么显然我们有

ans=bi+wi
那么
ans=i=1nwi+bii=1nmin{wi,bi}
那么我们可以这样跑个网络流求最小割,但是我们这个时候因为多出来了一个 pi的限制,那么
ans=i=1nwi+bi(bi+wi+pi)
我们就对于每一个节点 i新建一个节点 i然后连接一条流量为 pi的边给他同时我们让 (s,i)=bi (i,t)=wi这样我们的网络流可以分成 S T两个集合当 i属于 s的时候 i显然就是个黑色的了,我们这个时候让 i向所有的 k连边INF k满足 liakri那么我们分析一下发现因为存在最小割,那么就存在三种情况
  1. i为黑色,此时(s,i)为割边没有流量经过(i,i)
  2. i为白色,此时(i,i)为割边那么存在满足条件
  3. i为白色,此时(i,i)不是割边但是因为存在最小割那么所有满足条件的k均变成了(k,t)满流视作变成了黑色,所以仍然满足条件。

因为n太大,我们要进行优化,我们可以发现对于每一次由i连接到的k如果按照ai排序均为连续的一个区间,我们可以使用线段树维护,我们这样看作,[l,r]表示为lair中的每一个i提供了一个插头,我们维护的就是一个放满了插头的树,但是因为有先后顺序所以我们用可持久化线段树维护,同时如果我们出现了两个相同的ai怎么办呢,如何让两个i公用一个插头呢,显然将当前的插头连一条INF的边给上一个需要当前位置插头的插头就行了

ans=i=1nwi+biflow(s,t)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 5000;
const int MAXLOG = 13;
const int INF = 0x7fffffff;
struct node{
    int c,v;
    node *next,*back;
}*adj[MAXN*20],edge[1000005],*ecnt=edge;
int n,a[MAXN+10],b[MAXN+10],w[MAXN+10],_l[MAXN+10],_r[MAXN+10],_p[MAXN+10],tmp[MAXN*3+10],m,s,t,ans,sump,dis[MAXN*20],num[MAXN*20], roots[MAXN+10], pcnt;
struct tnode{
    int ch[2];
}pool[MAXN*MAXLOG+10];
inline void addedge(int u, int v, int c)
{
    ++ecnt;
    ecnt->v = v;
    ecnt->c = c;
    ecnt->next = adj[u];
    ecnt->back = ecnt+1;
    adj[u] = ecnt;
    ++ecnt;
    ecnt->v = u;
    ecnt->c = 0;
    ecnt->next = adj[v];
    ecnt->back = ecnt-1;
    adj[v] = ecnt;
}
void Insert(int &x,int y,int l,int r,int i)
{
    x=++pcnt;
    pool[x]=pool[y];

    if(l==r)
    {
        addedge(x+t,i,INF);

        if(y) addedge(x+t,y+t,INF);         //处理相同的a[i]
        return;
    }
    int mid=(l+r)>>1;
    if(a[i]>mid) Insert(pool[x].ch[1],pool[y].ch[1],mid+1,r,i);
    else Insert(pool[x].ch[0],pool[y].ch[0],l,mid,i);
    if(pool[x].ch[1]) addedge(t+x,t+pool[x].ch[1],INF);
    if(pool[x].ch[0]) addedge(t+x,t+pool[x].ch[0],INF);
}
void Link(int x,int l,int r,int i)
{
    if(l>_r[i]||_l[i]>r)
        return;
    if(_l[i]<=l&&_r[i]>=r)
    {
        addedge(n+i,t+x,INF);
        return;
    }
    int mid=(l+r)>>1;
    if(pool[x].ch[0]) Link(pool[x].ch[0],l,mid,i);
    if(pool[x].ch[1]) Link(pool[x].ch[1],mid+1,r,i);
}
int isap(int u,int augu)
{
    if(u==t) return augu;
    int augv=0,v,mind=sump-1,delta;
    for(node *p=adj[u]; p; p=p->next)
    {
        v=p->v;
        if(p->c)
        {
            if(dis[u]==dis[v]+1)
            {
                delta=min(augu-augv,p->c);
                delta=isap(v,delta);
                augv+=delta;
                p->c-=delta;
                p->back->c+=delta;
                if(augv==augu||dis[s]>=sump)
                    return augv;
            }
            mind=min(mind,dis[v]);
        }
    }
    if(!augv)
    {
        if(!--num[dis[u]])
            dis[s]=sump;
        dis[u]=mind+1;
        num[dis[u]]++;
    }
    return augv;
}
queue<int> que;
int work()
{
    que.push(t);
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        for(node *p=adj[u]; p; p=p->next)
        {
            if(!dis[p->v])
            {
                dis[p->v] = dis[u] + 1;
                que.push(p->v);
            }
        }
    }
    dis[t] = 0;
    for(int i=1; i<=sump; i++)
    {
        if(dis[i] == 0)
        {
            dis[i] = sump;
            continue;
        }
        num[dis[i]]++;
    }
    num[dis[t]=0]++;
    int ret = 0;
    while(dis[s] < sump)
        ret += isap(s, INF);
    return ret;
}
inline void Read(int &u)
{
    char ch;
    while((ch = getchar()), ch<'0'||ch>'9');
    u = ch - '0';
    while((ch = getchar()), ch>='0'&&ch<='9') u = u*10+ch-'0';
    ungetc(ch, stdin);
}
int main()
{
    Read(n);
    for(int i=1; i<=n; i++)
    {
        Read(a[i]);
        Read(b[i]);
        Read(w[i]);
        Read(_l[i]);
        Read(_r[i]);
        Read(_p[i]);
        //scanf("%d%d%d%d%d%d", &a[i], &b[i], &w[i], &_l[i], &_r[i], &_p[i]);
        tmp[++m] = a[i];
        tmp[++m] = _l[i];
        tmp[++m] = _r[i];
        ans += w[i] + b[i];
    }
    sort(tmp+1, tmp+1+m);
    m = unique(tmp+1, tmp+1+m) - tmp - 1;
    s = 2 * n + 1, t = s + 1;
    for(int i=1; i<=n; i++)
    {
        a[i] = lower_bound(tmp+1, tmp+1+m, a[i]) - tmp;
        _l[i] = lower_bound(tmp+1, tmp+1+m, _l[i]) - tmp;
        _r[i] = lower_bound(tmp+1, tmp+1+m, _r[i]) - tmp;
        addedge(s, i, b[i]);
        addedge(i, t, w[i]);
        addedge(i, i+n, _p[i]);
    }
    for(int i=1; i<=n; i++)
    {
        if(roots[i-1] != 0) Link(roots[i-1], 1, m, i);
        Insert(roots[i], roots[i-1], 1, m, i);
    }
    sump = t + pcnt;
    printf("%d\n", ans - work());
    return 0;
}

转载于:https://www.cnblogs.com/JeremyGJY/p/5921595.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值