[ONTAK2015]Bajtman i Okrągły Robin

bzoj 4276: [ONTAK2015]Bajtman i Okrągły Robin

Time Limit: 40 Sec  Memory Limit: 256 MB

Description

有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2],...,[b[i]-1,b[i]]这么多段长度为1时间中选出一个时间进行抢劫,并计划抢走c[i]元。作为保安,你在每一段长度为1的时间内最多只能制止一个强盗,那么你最多可以挽回多少损失呢?

Input

第一行包含一个正整数n(1<=n<=5000),表示强盗的个数。
接下来n行,每行包含三个正整数a[i],b[i],c[i](1<=a[i]<b[i]<=5000,1<=c[i]<=10000),依次描述每一个强盗。

Output

输出一个整数,即可以挽回的损失的最大值。

Sample Input

4
1 4 40
2 4 10
2 3 30
1 3 20

Sample Output

90
费用流
最容易想到的建图方法:
 
 
TLE
主要是因为一个强盗向一个区间内的所有点连边,太多了
所以我们考虑优化这些连边
线段树
建图方法:
 
然后跑最大费用即可
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#define N 50001
#define M N*11
#define inf 1e9
using namespace std;
int n,a[N],b[N],c[N],maxn;
int id[N*4],num,tot=1;
int src,dec,ans;
int front[N*6],from[M],next[M],to[M],cap[M],cost[M],pre[N*6];
int dis[N*6];
bool v[N*6];
int lc[N*6],rc[N*6];
queue<int>q;
struct node
{
    int l,r;
}tr[N*4];
void add(int u,int v,int w,int val)
{
    to[++tot]=v;next[tot]=front[u];front[u]=tot;from[tot]=u;cap[tot]=w;cost[tot]=val;
    to[++tot]=u;next[tot]=front[v];front[v]=tot;from[tot]=v;cap[tot]=0;cost[tot]=-val;
}
void build(int k,int l,int r)
{
    id[k]=++num;
    tr[k].l=l;tr[k].r=r;
    if(l==r) return;
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build((k<<1)+1,mid+1,r);
    add(id[k<<1],id[k],mid-l+1,0);
    add(id[(k<<1)+1],id[k],r-mid,0);
}
void insert(int k,int l,int r,int wh)
{
    if(tr[k].l==l&&tr[k].r==r)
    {
        add(wh,id[k],1,c[wh-num]);
        return;
    }
    int mid=tr[k].l+tr[k].r>>1;
    if(r<=mid) insert(k<<1,l,r,wh);
    else if(l>mid) insert((k<<1)+1,l,r,wh);
    else {insert(k<<1,l,mid,wh);insert((k<<1)+1,mid+1,r,wh);}
}
bool spfa()
{
    memset(dis,-127,sizeof(dis));
    q.push(src);v[src]=true;dis[src]=0;
    while(!q.empty())
    {
        int now=q.front();q.pop();v[now]=false;
        for(int i=front[now];i;i=next[i])
        {
            int t=to[i];
            if(dis[t]<dis[now]+cost[i]&&cap[i]>0)
            {
                dis[t]=dis[now]+cost[i];
                pre[t]=i;
                if(!v[t])
                {
                    q.push(t);
                    v[t]=true;
                }
            }
        }
    }
    return dis[dec]>0;
}
void mcmf()
{
    while(spfa())
    {
        int tmp=inf;
        for(int i=pre[dec];i;i=pre[from[i]]) tmp=min(tmp,cap[i]);
        for(int i=pre[dec];i;i=pre[from[i]]) 
        {
            cap[i]-=tmp;cap[i^1]+=tmp;
        }
        ans+=tmp*dis[dec];
    }
    printf("%d",ans);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d%d%d",&a[i],&b[i],&c[i]);
        b[i]--;
        maxn=max(maxn,b[i]);
    }
    dec=n*6-1;
    build(1,1,maxn);
    for(int i=1;i<=n;i++) 
     add(src,num+i,1,0);
    add(1,dec,maxn,0);
    for(int i=1;i<=n;i++) 
     insert(1,a[i],b[i],i+num);
    mcmf();
}

dis数组开始初始化为-1,应该是-inf,因为反向弧的费用为负值,未访问过的点要足够小才能完成回流

posted @ 2017-03-07 15:09  TRTTG  阅读(441)  评论(0编辑  收藏  举报