主席树 或者 离散化+分块 BZOJ 4636

4636: 蒟蒻的数列

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 381  Solved: 177
[Submit][Status][Discuss]

Description

蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列
题目描述
DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知
道N次操作后数列中所有元素的和。他还要玩其他游戏,所以这个问题留给你解决。
 

 

Input

第一行一个整数N,然后有N行,每行三个正整数a、b、k。
N<=40000 , a、b、k<=10^9
 

Output

一个数,数列中所有元素的和
 

 

Sample Input

4
2 5 1
9 10 4
6 8 2
4 6 3

Sample Output

16

 

 

思路一: 主席树(刚开始没想到,我好菜呀,明明这两天主席树还刚刚碰到的)

因为一共只有40000个操作,而且a和b的询问区间特别大,所以所有的更新节点不会太多,那么我们就用主席树,因为主席树每次最多就建立log个节点,所以能否符合要求。所以就用主席树打标记,然后如果lnub=0,那么就ans+=(mid-l+1)*val,如果rnub=0,那么ans+=(r-mid)*val,如果lnub=0&&rnub=0,那么ans+=(r-l+1)*val;即可完成query操作

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int inf = 1e9 + 5;
const int maxn = 40000 + 5;
int n, root[maxn], tot;
struct Tree{
    int lnub, rnub, val;
}tree[maxn * 100];
LL ans;

int update(int o, int ql, int qr, int l, int r, int cost){
    int k = ++tot;
    tree[k] = tree[o];
    if (ql <= l && qr >= r) {
        tree[k].val = max(tree[k].val, cost);
        return k;
    }
    int mid = (l + r) / 2;
    if (ql <= mid) tree[k].lnub = update(tree[o].lnub, ql, qr, l, mid, cost);
    if (qr > mid) tree[k].rnub = update(tree[o].rnub, ql, qr, mid + 1, r, cost);
    return k;
}

void push_down(int o){
    int lb = tree[o].lnub, rb = tree[o].rnub;
    tree[lb].val = max(tree[lb].val, tree[o].val);
    tree[rb].val = max(tree[rb].val, tree[o].val);
}

void query(int o, int l, int r){
    if (!tree[o].lnub && !tree[o].rnub){
        ans = ans + 1LL * (r - l + 1) * tree[o].val;
        return ;
    }
    push_down(o);
    int mid = (l + r) / 2;
    if (tree[o].lnub) query(tree[o].lnub, l, mid);
    if (tree[o].rnub) query(tree[o].rnub, mid + 1, r);
    if (!tree[o].lnub) ans = ans + 1LL * (mid - l + 1) * tree[o].val;
    if (!tree[o].rnub) ans = ans + 1LL * (r - mid) * tree[o].val;
}

int main(){
    cin >> n;
    for (int i = 1; i <= n; i++){
        int a, b, k; scanf("%d%d%d", &a, &b, &k);
        root[i] = update(root[i - 1], a, b-1, 1, inf, k);
    }
    query(root[n], 1, inf);
    cout << ans << endl;
    return 0;
}
View Code

 

关键:区间的大小

 

思路二:分块

这道题分块不难想,对于每个区间如果某一个块是完整的,就令他maxval[当前块的id]=k。然后如果询问的是不完整的,就暴力的对这个块进行重构。所以这样的复杂度最坏应该是3*n^1.5。然后关键的麻烦的就是如果两个区间之间如何求值啊,我不会啊,感觉自己写的好麻烦啊——不过这里呢,我看了卿学姐的想法,感觉好神啊,这里就介绍一下咯。

我们把所有的区间都扔进去,因为询问的区间是[a,b),那么,我们就每次把a,b-1,b这三个元素都扔到这个区间里面去就好啦,然后每次询问的时候就是将[a,b-1),[b-1,b)这样子更新就好了。感觉超级超级方便,都不需要特判两边的区间是否相等之类的乱七八糟的东西了

 

卿学姐的:链接

 

posted @ 2017-03-04 20:53  知る奇迹に  阅读(249)  评论(0编辑  收藏  举报