UOJ169. 【UR #11】元旦老人与数列

来源:互联网 发布:淘宝达人机构申请 编辑:程序博客网 时间:2024/04/29 12:08

UOJ169. 【UR #11】元旦老人与数列

线段树


UOJ传送点


((٩(//̀Д/́/)۶))这是我见过的最恶心的线段树,最开始还以为有什么巧妙的办法,所以这个blog要比其他的长点,大家耐心<(▰˘◡˘▰)>点看

迷之元旦老人的背景就不管了,就是给你一个数列,4种操作:一段区间加个数,一段区间所有值与一个值取个max,区间查询最小值,因为B数组是单调递减的(与A数组取min),B区间的查询就是求A的历史最小值

暴力O(NM)能得十分(好少),还有二十分没有max操作,线段树模板题,多记一个历史最小值就行。

后面的分数注定这是一道神题 \(▔^▔)/,其实写法很暴力,主要是复杂度的证明很麻烦,因为正解感觉很多都是暴力修改,好像过不了的样子。

题解

我们定义一个函数f(t)表示t这棵线段树的势能(势能想表达的就是线段树上节点的值的大小的混乱情况,势能越大,也就越混乱,简单表达为:f(t)=Ni=1(1(vivleftson)+1(vivrightson))),每一个加操作,最多会让树的势能+2,可以画个楼梯形状的图看看,而每一次max操作,可能会让势能减少,于是根据这个,我们制定一个线段树算法。

线段树上就不只维护lazy标记,最小值等等,我们需要维护这几样东西:

  • vai:区间最小值
  • lai:最小值的lazy标记
  • sai:最小值的lazy标记的最小前缀和(为了维护历史最小值)
  • vbi:区间严格次小值(没有就让它等于极大值INF
  • lbi:严格次小值的lazy标记
  • sbi:严格次小值的lazy标记的最小前缀和
  • hi:历史最大值

因为max操作在改变两个值及其以上时就会降低势能,所以为了节省时间,我们就在f(t)要减少2(要改变两个值)的时候再进行修改,于是就要维护最小值与次小值,加操作和以前一样,两个值两个lazy标记都要变,遇到max操作时,如果只改变一个值(vbiD,次小值比D大),我们就给最小值的标记加上Dvai,当然vaiD就不用管啦,然后继续后面的操作,如果vbi<D,直接暴力向下修改,直到只用改变一个值的时候。

既然是暴力,那么其中一次修改复杂度可能会达到O(N),那么总复杂度就会达到O(NM),但是,如果你这次全部改完,f(t)就会变得很小,下一次就改的很少了,所以说是其实是均摊O(log2N)

那个lazy标记的前缀和最小值是干什么的?维护历史最小值啊,在下传标记的时候,比如最小值的维护(先不说次小值):hi=min(hi,vai+safather)vai=vai+lafatherlai=lai+safather。还有啊,在下传标记的时候,最小值和次小值要分开传,最小值在哪一棵子树上,才往那子树传,否则只传次小值。

这么霸气的线段树连初始化都要好好想一下(其实差不多,反正想一下就行,blog再写就太长了,我都没有耐心写了 ˋ(′~`”)ˊ),看看代码就行。

代码调了好久,一开始没用堆行线段树,常数很大,比别人跑得慢,极限数据直接T了,我自己的随机数据直接8秒,然后没管这道题了,后来有调了很久,结果迷之AC,什么都没改,迷,( ̄ε  ̄),但是自己出的数据还是会超2S,可能是UOJ感觉这种线段树常数比较大,所以N给的小吧。

#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;}
1 0
原创粉丝点击