主席树 或者 离散化+分块 BZOJ 4636
4636: 蒟蒻的数列
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 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
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; }
关键:区间的大小
思路二:分块
这道题分块不难想,对于每个区间如果某一个块是完整的,就令他maxval[当前块的id]=k。然后如果询问的是不完整的,就暴力的对这个块进行重构。所以这样的复杂度最坏应该是3*n^1.5。然后关键的麻烦的就是如果两个区间之间如何求值啊,我不会啊,感觉自己写的好麻烦啊——不过这里呢,我看了卿学姐的想法,感觉好神啊,这里就介绍一下咯。
我们把所有的区间都扔进去,因为询问的区间是[a,b),那么,我们就每次把a,b-1,b这三个元素都扔到这个区间里面去就好啦,然后每次询问的时候就是将[a,b-1),[b-1,b)这样子更新就好了。感觉超级超级方便,都不需要特判两边的区间是否相等之类的乱七八糟的东西了
卿学姐的:链接