bzoj4504 K个串

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4504

【题解】

我们初始考虑是能不能O(logn)内求出区间[l,r]内的答案,我们对权值建主席树然后差分(但是不行)。

所以必须改变思路(!)

对于每个右端点i,把它所能包含的左端点区间看成主席树来维护的区间。

再考察修改了啥,就能发现是区间修改,然后单个线段树查询。

最后套用“超级钢琴”做法即可。

注意主席树复制标记!!

# include <queue>
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, A[M], pre[M];
struct pa {
    int x, id;
    pa() {}
    pa(int x, int id) : x(x), id(id) {}
    friend bool operator < (pa a, pa b) {
        return a.x < b.x || (a.x == b.x && a.id < b.id);
    }
}a[M];

struct pb {
    ll w; int ps;
    pb() {}
    pb(ll w, int ps) : w(w), ps(ps) {}
    friend bool operator > (pb a, pb b) {
        return a.w>b.w;
    }
    friend bool operator < (pb a, pb b) {
        return a.w<b.w;
    }
    friend pb operator + (pb a, ll b) {
        return pb(a.w+b, a.ps);
    }
};

int rt[M];
namespace CMT {
    const int M = 15000000 + 10;

    ll tg[M]; pb w[M];
    int ch[M][2], siz=0;    
    
    inline void build(int &x, int l, int r) {
        x = ++siz; tg[x] = 0; w[x] = pb(0ll, l); 
        if(l == r) return;
        int mid = l+r>>1;
        build(ch[x][0], l, mid); build(ch[x][1], mid+1, r);
    }
    inline void edt(int &x, int y, int l, int r, int L, int R, int ad) {
        if(L>R) return;
        x = ++siz;
        ch[x][0] = ch[y][0]; ch[x][1] = ch[y][1]; tg[x] = tg[y];
        if(L <= l && r <= R) {
            w[x] = w[y] + (ll)ad;
            tg[x] = tg[y] + ad;
            return ;
        }
        int mid = l+r>>1;
        if(R <= mid) edt(ch[x][0], ch[y][0], l, mid, L, R, ad);
        else if(L > mid) edt(ch[x][1], ch[y][1], mid+1, r, L, R, ad);
        else {
            edt(ch[x][0], ch[y][0], l, mid, L, mid, ad);
            edt(ch[x][1], ch[y][1], mid+1, r, mid+1, R, ad);
        }
        if(w[ch[x][0]] > w[ch[x][1]]) w[x] = w[ch[x][0]]; else w[x] = w[ch[x][1]];
        w[x] = w[x] + tg[x];
    }
    
    inline pb ask(int x, int l, int r, int L, int R) {
        if(L <= l && r <= R) return w[x];
        int mid = l+r>>1;
        if(R <= mid) return ask(ch[x][0], l, mid, L, R) + tg[x];
        else if(L > mid) return ask(ch[x][1], mid+1, r, L, R) + tg[x];
        else {
            pb a, b;
            a = ask(ch[x][0], l, mid, L, mid), b = ask(ch[x][1], mid+1, r, mid+1, R);
            if(b > a) a = b;
            return a + tg[x];
        }
    }    
}

struct paa {
    int l, r, x, pos; ll va;
    paa() {}
    paa(int l, int r, int x, int pos, ll va) : l(l), r(r), x(x), pos(pos), va(va) {}
    friend bool operator < (paa a, paa b) {
        return a.va < b.va;
    }
};

priority_queue<paa> q;

int main() {
    scanf("%d%d", &n, &m);
    for (int i=1, x; i<=n; ++i) {
        scanf("%d", &x);
        a[i] = pa(x, i);
        A[i] = x;
    }
    sort(a+1, a+n+1);
    for (int i=1, j; i<=n; i=j+1) {
        j = i;
        pre[a[i].id] = 0;
        while(a[j+1].x == a[j].x) {
            pre[a[j+1].id] = a[j].id;
            ++j;
        }
    }
    CMT::build(rt[0], 1, n);
    for (int i=1; i<=n; ++i) CMT::edt(rt[i], rt[i-1], 1, n, pre[i]+1, i, A[i]);
    pb t;
    for (int i=1; i<=n; ++i) {
        t = CMT::ask(rt[i], 1, n, 1, i);
        q.push(paa(1, i, i, t.ps, t.w));
    }
    for (int i=1; i<m; ++i) {
        paa tp = q.top(); q.pop();
//        printf("%d %d %d %d %lld\n", tp.l, tp.r, tp.x, tp.pos, tp.va);
        if(tp.l < tp.pos) {
            t = CMT::ask(rt[tp.x], 1, n, tp.l, tp.pos-1);
            q.push(paa(tp.l, tp.pos-1, tp.x, t.ps, t.w));
        }
        if(tp.pos < tp.r) {
            t = CMT::ask(rt[tp.x], 1, n, tp.pos+1, tp.r);
            q.push(paa(tp.pos+1, tp.r, tp.x, t.ps, t.w));
        }
    }
    printf("%lld\n", q.top().va);

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/galaxies/p/bzoj4504.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值