HNOI2016模拟 disk

题目大意

现在有 A,B 两个工厂,你有 n 天的时间去生产 K 张光盘。一开始每张光盘都没有被加工,对于一张光盘,你需要先把他送到 A 工厂加工,然后将加工后的光盘送到 B 工厂再加工,最终生产出来,注意对于一个工厂每天最多加工一张光盘,但是一天内你可以将一张光盘从 A 加工再送到 B 处加工。对于 A 工厂,其第 i 天加工一张光盘的代价为 Ai B 工厂为 Bi 。给定 N,K,Ai,Bi ,问生产 K 张光盘的最少代价为多少。

数据范围

1KN105
0Ai,Bi109

题解

我们先来看一个搞笑的做法。将 A 工厂拆为 n 个点,分别表示每一天的 A 工厂,记为 Ai ,同理将 B 工厂拆为 n 个点,接着原点 S Ai 连流量为1费用为 Ai 的边, Ai Bi 连流量为1费用为0的边, Bi 向汇点 T 连流量为1费用为 Bi 的边, Bi Bi+1 连流量为正无穷费用为0的边,然后跑一遍总流量为 K 的最小费用最大流即可。
这个算法的正确性比较显然,一个光盘可以视为1个流量,并且一个光盘必须在 Ai,Bj 生产,满足 ij
接下来我们基本上都是立足于这个基本模型来解决问题。
不妨将每个 Ai 视为1个左括号,将每个 Bi 视为一个右括号。一开始他们排成了 ()()()
我们最终的方案,相当于从这个序列中提取出一个权值最小的子序列,并且他是一个合法的括号序列,包含恰有 K 个左括号与右括号。
首先我们每次必然可以取出一个 () ,将他们加入当前的序列中。这样必然是合法的。
但考虑另外一种情况,我们是可以取出 )( ,比如说一开始的序列为 (()) ,加入 )( 就变成了 (()()) ,这也是一个合法的情况。
不妨将 ( 视为+1,将 ) 视为-1,记 Si 表示前 i 个字符的前缀和。那么一个合法的括号序列,必须满足 jSj0,SN=0 ,注意到最后一个条件,假如序列中左括号与右括号数量相同,那是必然满足的。
考虑假如当前取出 () ,位置分别为 L,R ,那么相当于对 [L,R1] Si 都加上1.
假如当前取出 )( ,那么相当于对 [L,R1] Si 都减去1,因为要合法,那么当前的 )( 合法的充要条件为 minR1i=L(Si)>0
接下来的做法就比较高能了。
我们不妨用线段树来维护一些(dui)值。
设当前结点代表的区间为 [L,R]
1. ma 表示权值最小的 (
2. mb 表示权值最小的 )
3. lr 表示权值最小的 ()
4. rl 表示权值最小的 )(
5. lrl 表示权值最小的 )( ,设位置为 l,r ,必须满足 minr1i=lSi>minRi=LSi
6. min 表示区间最小的 Si
7. la 表示权值最小的 ( ,设位置为 l ,满足 minli=LSi>minRi=LSi
8. lb 表示权值最小的 ) ,设位置为 r ,满足 minRi=rSi>minRi=LSi
9. tag 表示区间中的 Si 都要加上 tag

那么这样我们就可以轻松(蛤铪)进行合并了。最终合法的 )( 就相当于 [1,N] lrl ,因为 SN 必然等于0。假如当前要合并 p=[L,mid],q=[mid+1R] ,我们分情况讨论 p.min<q.min,p.min=q.min,p.min>q.min 即可。比如当前 p.min<q.min ,那么 la=p.la ,因为假如跨越了 p ,那么最小值就必然等于区间最小值了。 lb 可以是 p.lb,q.mb lrl 可以是 p.lrl,p.lb+q.ma 。以此类推。

最终我们每次取出最小的 () )( ,模拟一下就好了。算法的正确性可以从一开始搞笑的做法推断出来。
总的时间复杂度为 O(nlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fe first
#define se second

using namespace std;

const int MAXN = 200005;

typedef pair<int,int> P;

struct Node
{
    P lr,rl,lrl;
    int Min,tag,ma,mb,la,lb;
}T[MAXN * 4];

int S[MAXN],N,K;

P operator +(const P &a,const P &b)
{
    return S[a.fe] + S[a.se] < S[b.fe] + S[b.se] ? a : b;
}

int mint(int a,int b)
{
    return S[a] < S[b] ? a : b;
}

Node operator +(const Node &a,const Node &b)
{
    Node tmp;
    tmp.lr = (a.lr + b.lr) + P(a.ma,b.mb);
    tmp.rl = (a.rl + b.rl) + P(a.mb,b.ma);
    tmp.ma = mint(a.ma,b.ma),tmp.mb = mint(a.mb,b.mb);
    tmp.Min = min(a.Min,b.Min);
    if (a.Min < b.Min)
    {
        tmp.la = a.la;
        tmp.lb = mint(a.lb,b.mb);
        tmp.lrl = a.lrl + (b.rl + P(a.lb,b.ma));
    }
    if (a.Min == b.Min)
    {
        tmp.la = a.la,tmp.lb = b.lb;
        tmp.lrl = a.lrl + b.lrl + P(a.lb,b.la);
    }
    if (a.Min > b.Min)
    {
        tmp.la = mint(b.la,a.ma);
        tmp.lb = b.lb;
        tmp.lrl = a.rl + P(a.mb,b.la) + b.lrl;
    }
    tmp.tag = 0;
    return tmp;
}

void Mark(int jd,int tag)
{
    T[jd].Min += tag;
    T[jd].tag += tag;
}

void Lazy_Down(int jd)
{
    if (!T[jd].tag) return;
    Mark(jd << 1,T[jd].tag),Mark(jd << 1 | 1,T[jd].tag);
    T[jd].tag = 0;
}

void Build(int l,int r,int jd)
{
    if (l == r)
    {
        T[jd].Min = 0;
        if (l & 1) T[jd].ma = l; else T[jd].mb = l;
        return;
    }
    int mid = l + r >> 1;
    Build(l,mid,jd << 1),Build(mid + 1,r,jd << 1 | 1);
    T[jd] = T[jd << 1] + T[jd << 1 | 1];
}

void Change(int l,int r,int i,int j,int s,int jd)
{
    if (j < l || i > r) return;
    if (i <= l && r <= j) Mark(jd,s); else
    {
        int mid = l + r >> 1;
        Lazy_Down(jd);
        Change(l,mid,i,j,s,jd << 1),Change(mid + 1,r,i,j,s,jd << 1 | 1);
        T[jd] = T[jd << 1] + T[jd << 1 | 1];
    }
}

void Upd(int l,int r,int p,int jd)
{
    if (l == r) return;
    int mid = l + r >> 1;
    Lazy_Down(jd);
    if (p <= mid) Upd(l,mid,p,jd << 1); else
        Upd(mid + 1,r,p,jd << 1 | 1);
    T[jd] = T[jd << 1] + T[jd << 1 | 1];
}

int main()
{
    scanf("%d%d", &N, &K);
    for(int i = 1,x = 0;i <= N;i ++)
    {
        scanf("%d", &x);
        S[(i * 2) - 1] = x;
    }
    for(int i = 1,x = 0;i <= N;i ++)
    {
        scanf("%d", &x);
        S[(i * 2)] = x;
    }
    S[0] = (1 << 30) - 1;
    Build(1,2 * N,1);
    long long ans = 0;
    for(int i = 1;i <= K;i ++)
    {
        P cr = T[1].lr + T[1].lrl;
        int x = cr.fe,y = cr.se;
        if (x & 1) Change(1,2 * N,x,y - 1,1,1); else
            Change(1,2 * N,x,y - 1,-1,1);
        ans += S[x] + S[y];
        S[x] = S[y] = (1 << 30) - 1;
        Upd(1,2 * N,x,1),Upd(1,2 * N,y,1);
    }
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(HNOI2016模拟 disk)