BZOJ 4636: 蒟蒻的数列 分快,int64线段树

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

解法1:动态开点线段树,直接在线段树上打标记,最后dfs一遍整棵线段树即可.因为区间长度比较大,所以需要动态开点.注意一些没有建立的节点也是有贡献的,要把这些加进去.

//151288 kb  788ms O(nsqrt(n))
#include 
using namespace std;
const int maxn = 50000;
const int inf = 1e9;
long long ans;
int n, a, b, k;
namespace int64segmenttree{
    int sum[maxn<<8], son[maxn<<8][2], root, sz;
    void insert(int &rt, int l, int r, int L, int R, int x){
        if(L > R) return;
        if(!rt) rt = ++sz;
        if(l == L && r == R){
            sum[rt] = max(sum[rt], x);
            return ;
        }
        int mid = (l + r) / 2;
        if(R <= mid) insert(son[rt][0], l, mid, L, R, x);
        else if(L > mid) insert(son[rt][1], mid + 1, r, L, R, x);
        else{
            insert(son[rt][0], l, mid, L, mid, x);
            insert(son[rt][1], mid + 1, r, mid + 1, R, x);
        }
    }
    void query(int rt, int l, int r, int x){
        if(!rt) return ;
        sum[rt] = max(sum[rt], x);
        if(!son[rt][0] && !son[rt][1]){
            ans += 1LL * (r - l + 1) * sum[rt];
            return ;
        }
        int mid = (l + r) / 2;
        query(son[rt][0], l, mid, sum[rt]);
        query(son[rt][1], mid + 1, r, sum[rt]);
        if(!son[rt][0]) ans += 1LL * (mid - l + 1) * sum[rt];
        if(!son[rt][1]) ans += 1LL * (r - mid) * sum[rt];
    }
}
using namespace int64segmenttree;

int main(){
    int n, a, b, k;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d%d%d", &a, &b, &k);
        insert(root, 1, inf, a, b - 1, k);
    }
    query(root, 1, inf, 0);
    printf("%lld\n", ans);
    return 0;
}

解法2:离散化+分块 15072kb 2356ms

#include 
using namespace std;
const int maxn = 2e5+7;
int n, m, num, belong[maxn], block, l[maxn], r[maxn], low[maxn];
vector <int> V;//离散化
map <int, int> H1; //存下标
map <int, int> H2; //存值
int A[maxn], B[maxn], K[maxn], a[maxn];

void build(){
    block = sqrt(n);
    num = n / block; if(n%block) num++;
    for(int i = 1; i <= num; i++) l[i] = (i - 1) * block + 1, r[i] = i * block;
    r[num] = n;
    for(int i = 1; i <= n; i++) belong[i] = (i - 1) / block + 1;
}

void update(int L, int R, int k){
    if(L > R) return;
    if(belong[L] == belong[R]){
        for(int i = l[belong[L]]; i <= r[belong[L]]; i++) a[i] = max(a[i], low[belong[L]]); low[belong[L]] = 0;
        for(int i = L; i <= R; i++) a[i] = max(a[i], k);
        return;
    }
    for(int i = l[belong[L]]; i <= r[belong[L]]; i++) a[i] = max(a[i], low[belong[L]]); low[belong[L]] = 0;
    for(int i = l[belong[R]]; i <= r[belong[R]]; i++) a[i] = max(a[i], low[belong[R]]); low[belong[R]] = 0;
    for(int i = L; i <= r[belong[L]]; i++) a[i] = max(a[i], k);
    for(int i = l[belong[R]]; i <= R; i++) a[i] = max(a[i], k);
    for(int i = belong[L] + 1; i < belong[R]; i++) low[i] = max(low[i], k);
}

int main(){
    scanf("%d", &m);
    for(int i = 1; i <= m; i++){//懒得离散化,学卿学姐直接将每个询问扔3个点进去就好
        scanf("%d%d%d", &A[i], &B[i], &K[i]);
        V.push_back(A[i]), V.push_back(B[i]), V.push_back(B[i] - 1);
    }
    n = 3*m;
    build();
    sort(V.begin(), V.end()), V.erase(unique(V.begin(), V.end()), V.end());
    for(int i = 0; i < (int) V.size(); i++){
        H1[V[i]] = i + 1, H2[i + 1] = V[i];
    }
    long long ans = 0;
    for(int i = 1; i <= m; i++) update(H1[A[i]], H1[B[i] - 1], K[i]);
    for(int i = 0; i < (int) (V.size()); i++) a[i + 1] = max(a[i + 1], low[belong[i + 1]]);
    for(int i = 0; i < (int) (V.size() - 1); i++) ans += (H2[i + 2] - H2[i + 1]) * a[i + 1];
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(ACM/ICPC_,BZOJ,数据结构_分块法,数据结构_线段树,数据结构_树状数组)