zoukankan      html  css  js  c++  java
  • 主席树 或者 离散化+分块 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
    ")
    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)这样子更新就好了。感觉超级超级方便,都不需要特判两边的区间是否相等之类的乱七八糟的东西了

    卿学姐的:链接

  • 相关阅读:
    sb#run():
    aop编程,自定义注解参数和方法范围
    vue 工程化
    mybatis SqlSession
    java传时间
    树的同构
    串的模式匹配
    堆栈模拟队列
    银行业务队列简单模拟
    一元多项式的乘法与加法运算
  • 原文地址:https://www.cnblogs.com/heimao5027/p/6502658.html
Copyright © 2011-2022 走看看