您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: