LOJ 6032 「雅礼集训 2017 Day2」水箱

线段树合并+树状数组

把0设成-1,问题就变成求最大前缀和。

考虑一个DP,记f[i]表示i隔板隔住了水,i之前最多满足多少条件。转移的时候枚举j表示[j,i]能是一个以j,i为左右端点的装水区间。

这样的问题是每次从新的i扫到一个j都要合并一遍区间里的所有标记,也就是一个区间会被合并多次。然而能够证明,不同的装水区间不超过O(n)个,且它们之间不会有交(端点可能相同)。

因此先找出这些区间,线段树维护一个区间的所有条件,求最大前缀和,合并就是直接线段树合并,这样总O(nlogn)。

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 200005
#define lowbit(_i) (_i&-_i)
#define mkp(_i,_j) make_pair(_i,_j)
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<29;

    vector<pair<int,int> > vec[N], val[N];
    int n, m, h[N], sta[N], stacnt, incnt, arr[N], arrcnt, sum, mx, f[N], cnt0, rank[N];

    struct inter
    {
        int l, r, ans; inter *fa;
        bool operator < (const inter &that) const
        {
            return r == that.r ? l < that.l : r > that.r;
        }
    }in[N<<1];

    bool cmp_len(int a, int b){return in[a].r-in[a].l+1 < in[b].r-in[b].l+1;}

    struct BIT
    {
        int t[N*10];
        int query(int x) 
        {
            int r = 0; 
            for(; x; x -= lowbit(x)) 
            {
                in[r].l < in[t[x]].l ? r = t[x] : 0; 
            }
            return r;
        }
        void modi(int x, int v) 
        {
            for(; x < N; x += lowbit(x)) 
            {
                if(in[v].r < in[t[x]].r || (in[v].r == in[t[x]].r && in[t[x]].l < in[v].l))
                    t[x] = v;
            }
        }
    }T;

    struct SEG
    {
        int t[N*10];
        void build(int x, int l, int r)
        {
            if(l == r) {t[x] = h[l]; return;} int mid = (l+r)>>1;
            build(x<<1,l,mid); build(x<<1|1,mid+1,r);
            t[x] = max(t[x<<1], t[x<<1|1]);
        }
        int query(int x, int l, int r, int ql, int qr)
        {
            if(ql <= l && r <= qr) return t[x]; int mid = (l+r)>>1, ret = 0;
            if(ql <= mid) ret = max(ret, query(x<<1,l,mid,ql,qr));
            if(mid <  qr) ret = max(ret, query(x<<1|1,mid+1,r,ql,qr));
            return ret;
        }
    }S;

    struct seg
    {
        seg *ch[2];
        int sum, mx;
    }mem[N*20], *tot, *null, *root[N];
    seg *newseg()
    {
        seg *x = ++tot; *x = *null;
        return x;
    }
    void init()
    {
        null = tot = mem;
        null->ch[0] = null->ch[1] = null;
        null->sum = null->mx = 0;
        for(int i = 0; i <= incnt; i++) 
            root[i] = newseg();
    }
    void pushup(seg *x)
    {
        x->sum = x->ch[0]->sum + x->ch[1]->sum;
        x->mx = max(x->ch[0]->mx, x->ch[0]->sum + x->ch[1]->mx);
    }
    void insert(seg *x, int l, int r, int p, int v)
    {
        if(l == r){x->sum += v; x->mx = max(x->sum, 0); return;} int mid = (l+r)>>1;
        if(p <= mid)
        {
            if(x->ch[0] == null) x->ch[0] = newseg();
            insert(x->ch[0], l, mid, p, v);
        }
        else
        {
            if(x->ch[1] == null) x->ch[1] = newseg();
            insert(x->ch[1], mid+1, r, p, v);
        }
        pushup(x);
    }
    seg *merge(seg *x, seg *y, int l, int r)
    {
        if(x == null) return y;
        if(y == null) return x;

        seg *p = newseg(); 
        if(l == r)
        {
            p->sum = x->sum + y->sum;
            p->mx = max(0, p->sum);
            return p;
        }

        int mid = (l+r)>>1;
        p->ch[0] = merge(x->ch[0], y->ch[0], l, mid);
        p->ch[1] = merge(x->ch[1], y->ch[1], mid+1, r);
        pushup(p); 
        return p;
    }
    int query_sum(seg *x, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) return x->sum; int mid = (l+r)>>1, ret = 0;
        if(ql <= mid) ret += query_sum(x->ch[0], l, mid, ql, qr);
        if(mid <  qr) ret += query_sum(x->ch[1], mid+1, r, ql, qr);
        return ret;
    }
    void query_mx(seg *x, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) {mx = max(mx, sum + x->mx); sum += x->sum; return;} 
        int mid = (l+r)>>1;
        if(ql <= mid) query_mx(x->ch[0], l, mid, ql, qr);
        if(mid <  qr) query_mx(x->ch[1], mid+1, r, ql, qr);
    }

    void main()
    {
        int task; scanf("%d",&task);
        for(; task--; )
        {
            scanf("%d%d",&n,&m);
            for(int i = 1; i < n; i++) 
            {
                scanf("%d",&h[i]);
                arr[++arrcnt] = h[i];
                for(; stacnt && h[sta[stacnt]] <= h[i]; )
                {
                    in[++incnt] = (inter){sta[stacnt], i, 0, 0};
                    stacnt--;
                }
                in[++incnt] = (inter){sta[stacnt], i, 0, 0};
                sta[++stacnt] = i;
            }
            for(; ~stacnt; stacnt--) in[++incnt] = (inter){sta[stacnt], n, 0, 0};


            for(int i = 1, p, y, k; i <= m; i++)
            {
                scanf("%d%d%d",&p,&y,&k);
                cnt0 += k == 0;
                vec[p].push_back(mkp(y,k));
                arr[++arrcnt] = y;
            }

            arr[++arrcnt] = h[0] = h[n] = INF;
            arr[++arrcnt] = -1;

            sort(arr+1, arr+1+arrcnt);
            arrcnt = unique(arr+1, arr+1+arrcnt) - arr - 1;
            for(int i = 1; i <= n; i++) 
                for(int j = 0, jj = vec[i].size(); j < jj; j++) 
                    vec[i][j].first = lower_bound(arr+1, arr+1+arrcnt, vec[i][j].first) - arr;
            for(int i = 0; i <= n; i++) h[i] = lower_bound(arr+1, arr+1+arrcnt, h[i]) - arr;

            sort(in+1, in+1+incnt);
            in[0].l = -INF; in[0].r = INF;

            for(int i = 1; i <= incnt; i++)
            {
                in[i].fa = &in[T.query(in[i].l+1)];
                T.modi(in[i].l+1, i);
            }

            for(int i = 1; i <= incnt; i++) rank[i] = i; 
            sort(rank+1, rank+1+incnt, cmp_len);

            init(); 
            S.build(1,0,n);
            for(int ii = 1, i; ii <= incnt; ii++)
            {
                i = rank[ii];

                int d, u = min(h[in[i].l], h[in[i].r]);
                if(in[i].l + 1 == in[i].r)
                {
                    for(int j = 0, jj = vec[in[i].r].size(); j < jj; j++)
                    {
                        insert(root[i], 1, arrcnt, vec[in[i].r][j].first, vec[in[i].r][j].second == 1 ? 1 : -1);
                    }
                    d = 1;
                }
                else d = S.query(1,0,n,in[i].l+1, in[i].r-1);
                sum = 1 < d ? query_sum(root[i], 1, arrcnt, 1, d-1) : 0;
                mx = 0; query_mx(root[i], 1, arrcnt, d, u-1);
                in[i].ans = mx;
                val[in[i].r].push_back(mkp(in[i].l, mx));
                if(in[i].fa != &in[0]) root[in[i].fa-in] = merge(root[in[i].fa-in], root[i], 1, arrcnt);
            }
            for(int i = 1; i <= n; i++)
                for(int j = 0, jj = val[i].size(); j < jj; j++)
                    f[i] = max(f[i], f[val[i][j].first] + val[i][j].second);
            printf("%d\n",f[n]+cnt0);

            stacnt = arrcnt = incnt = cnt0 = 0;
            memset(f, 0, sizeof(f));
            memset(T.t, 0, sizeof(T.t));
            memset(S.t, 0, sizeof(S.t));
            for(int i = 0; i <= n; i++) vec[i].clear(), val[i].clear();

        }
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值