UOJ169. 【UR #11】元旦老人与数列
2016-01-29 20:14
253 查看
UOJ169. 【UR #11】元旦老人与数列
线段树
UOJ传送点
((٩(//̀Д/́/)۶))这是我见过的最恶心的线段树,最开始还以为有什么巧妙的办法,所以这个blogblog要比其他的长点,大家耐心<(▰˘◡˘▰)>点看
迷之元旦老人的背景就不管了,就是给你一个数列,4种操作:一段区间加个数,一段区间所有值与一个值取个max,区间查询最小值,因为B数组是单调递减的(与A数组取min),B区间的查询就是求A的历史最小值
暴力O(NM)O(NM)能得十分(好少),还有二十分没有maxmax操作,线段树模板题,多记一个历史最小值就行。
后面的分数注定这是一道神题 \(▔^▔)/,其实写法很暴力,主要是复杂度的证明很麻烦,因为正解感觉很多都是暴力修改,好像过不了的样子。
题解
我们定义一个函数f(t)f(t)表示tt这棵线段树的势能(势能想表达的就是线段树上节点的值的大小的混乱情况,势能越大,也就越混乱,简单表达为:f(t)=∑Ni=1(1(vi≠vleftson)+1(vi≠vrightson))f(t) = \sum_{i = 1}^{N}(1(v_{i} \neq v_{leftson}) + 1(v_{i} \neq v_{rightson}))),每一个加操作,最多会让树的势能+2,可以画个楼梯形状的图看看,而每一次maxmax操作,可能会让势能减少,于是根据这个,我们制定一个线段树算法。线段树上就不只维护lazylazy标记,最小值等等,我们需要维护这几样东西:
vaiva_{i}:区间最小值
laila_{i}:最小值的lazylazy标记
saisa_{i}:最小值的lazylazy标记的最小前缀和(为了维护历史最小值)
vbivb_{i}:区间严格次小值(没有就让它等于极大值INFINF)
lbilb_{i}:严格次小值的lazylazy标记
sbisb_{i}:严格次小值的lazylazy标记的最小前缀和
hih_{i}:历史最大值
因为maxmax操作在改变两个值及其以上时就会降低势能,所以为了节省时间,我们就在f(t)f(t)要减少2(要改变两个值)的时候再进行修改,于是就要维护最小值与次小值,加操作和以前一样,两个值两个lazylazy标记都要变,遇到maxmax操作时,如果只改变一个值(vbi≥Dvb_{i} \ge D,次小值比DD大),我们就给最小值的标记加上D−vaiD - va_{i},当然vai≥Dva_{i} \ge D就不用管啦,然后继续后面的操作,如果vbi<Dvb_{i} < D,直接暴力向下修改,直到只用改变一个值的时候。
既然是暴力,那么其中一次修改复杂度可能会达到O(N)O(N),那么总复杂度就会达到O(NM)O(NM),但是,如果你这次全部改完,f(t)f(t)就会变得很小,下一次就改的很少了,所以说是其实是均摊O(log2N)O(log_{2}N)。
那个lazylazy标记的前缀和最小值是干什么的?维护历史最小值啊,在下传标记的时候,比如最小值的维护(先不说次小值):hi=min(hi,vai+safather)h_{i} = min(h_{i}, va_{i} + sa_{father}),vai=vai+lafatherva_{i} = va_{i}+ la_{father},lai=lai+safatherla_{i} = la_{i} + sa_{father}。还有啊,在下传标记的时候,最小值和次小值要分开传,最小值在哪一棵子树上,才往那子树传,否则只传次小值。
这么霸气的线段树连初始化都要好好想一下(其实差不多,反正想一下就行,blogblog再写就太长了,我都没有耐心写了 ˋ(′~`”)ˊ),看看代码就行。
代码调了好久,一开始没用堆行线段树,常数很大,比别人跑得慢,极限数据直接T了,我自己的随机数据直接8秒,然后没管这道题了,后来有调了很久,结果迷之AC,什么都没改,迷,( ̄ε  ̄),但是自己出的数据还是会超2S,可能是UOJ感觉这种线段树常数比较大,所以NN给的小吧。
#include <iostream> #include <cstdlib> #include <cstdio> #include <ctime> using namespace std; const int maxn = 1000000 + 13, INF = 0x7fffffff; int n, m, task, a, b, c, root, pos, da[maxn]; int l[maxn], r[maxn], ls[maxn], rs[maxn], start; int va[maxn], vb[maxn], la[maxn], lb[maxn], sa[maxn], sb[maxn], his[maxn]; int min(int a, int b) {return a < b ? a : b;} int max(int a, int b) {return a > b ? a : b;} void read(int &a) { a = 0; bool judge = false, fu = false; char c; while((c = getchar()) != EOF) { if(c == '-') {fu = true; continue;} if(c == ' ' || c == '\n') { if(!judge) continue; if(fu) a *= -1; return; } a = a * 10 + (c - '0'); judge = true; } } void helpPush(int x, int i, bool is) { int LA = la[i], LB = lb[i], SA = sa[i], SB = sb[i]; if(!is) LA = LB, SA = SB; his[x] = min(his[x], va[x] + SA); va[x] += LA; if(vb[x] != INF) vb[x] += LB; sa[x] = min(sa[x], la[x] + SA); sb[x] = min(sb[x], lb[x] + SB); la[x] += LA; lb[x] += LB; } void pushDown(int i) { if(!la[i] && !lb[i] && sa[i] >= 0 && sb[i] >= 0) return; int LS = ls[i], RS = rs[i], VL = va[LS], VR = va[RS]; if(VL == VR) helpPush(LS, i, true), helpPush(RS, i, true); if(VL < VR) helpPush(LS, i, true), helpPush(RS, i, false); if(VL > VR) helpPush(LS, i, false), helpPush(RS, i, true); la[i] = lb[i] = sa[i] = sb[i] = 0; } void update(int i) { int LS = ls[i], RS = rs[i]; his[i] = min(his[LS], his[RS]); va[i] = min(va[LS], va[RS]); int a = max(va[LS], va[RS]), b = min(vb[LS], vb[RS]); if(va[LS] != va[RS]) vb[i] = min(a, b); else vb[i] = b; } void builtTree(int &i, int A, int B) { i = ++pos; l[i] = A; r[i] = B; if(A == B) { va[i] = da[A]; vb[i] = INF; his[i] = va[i]; return; } int mid = (A + B) >> 1; builtTree(ls[i], A, mid); builtTree(rs[i], mid + 1, B); update(i); } void plusOnTree(int i, int A, int B, int val) { int L = l[i], R = r[i]; if(R < A || L > B) return; if(A <= L && R <= B) { va[i] += val; if(vb[i] != INF) vb[i] += val; la[i] += val; lb[i] += val; sa[i] = min(sa[i], la[i]); sb[i] = min(sb[i], lb[i]); his[i] = min(his[i], va[i]); return; } pushDown(i); plusOnTree(ls[i], A, B, val); plusOnTree(rs[i], A, B, val); update(i); } void maxOnTree(int i, int A, int B, int val) { int L = l[i], R = r[i]; if(R < A || L > B) return; if(A <= L && R <= B && vb[i] > val) { if(va[i] >= val) return; la[i] += (val - va[i]); va[i] = val; return; } pushDown(i); maxOnTree(ls[i], A, B, val); maxOnTree(rs[i], A, B, val); update(i); } int Query(int i, int A, int B, bool HIS) { int L = l[i], R = r[i]; if(R < A || L > B) return INF; if(A <= L && R <= B) { if(HIS) return his[i]; return va[i]; } pushDown(i); int temp = min(Query(ls[i], A, B, HIS), Query(rs[i], A, B, HIS)); update(i); return temp; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) read(da[i]); builtTree(root, 1, n); for(int i = 1; i <= m; i++) { read(task); read(a); read(b); if(task <= 2) read(c); if(task == 1) plusOnTree(root, a, b, c); if(task == 2) maxOnTree(root, a, b, c); if(task == 3) printf("%d\n", Query(root, a, b, false)); if(task == 4) printf("%d\n", Query(root, a, b, true)); } return 0; }
相关文章推荐
- SpringMVC返回json数据的三种方式
- hibernate 普通字段延迟加载无效的解决办法
- C#学习——三种语句结构
- 动态规划 DEMO 投资分配问题
- CocoaPods的安装使用和常见问题
- Linux vi编辑器(鸟哥的Linux私房菜摘录)
- mac/linux 下kafka安装
- Xcode的基本调试之lldb
- 数据分析概览01:读《深入浅出数据分析》
- 2016-01-29 : csdn 迁移到 hexo
- c#之枚举类型和int以及string类型的转换
- Codeforces 435B. Pasha Maximizes
- ORACLE中null的排序问题
- Swift中NSData与NSDictionary之间的相互转换
- hdu 5273(递推)
- oracle connect by用法
- HDU3584(树状数组)
- Placing Lampposts
- C++ lambda笔记
- 体绘制之光线投射算法(附源码)