P6812 「MCOI-02」Ancestor 先辈

这个题真的是太恶心了……

一份同样的代码,在不同的时间提交,一次能过,一次差一点点……

第35个点是真nm恶心……(就挂在这个点上:一次950+ms,一次1.00s),刚好小号过了,大号没过……

于是重新写了一遍,貌似没有区别,但是第35个点只需要250+ms左右就能跑完。

那就用重新写的这一边来一发题解⑧。

题目大意

我们有一个序列,并且有两种操作:

第一种操作:将区间\([L, R]\)中间的数每一个都加上\(x\)

第二种操作:判断这一个序列是不是先辈。

什么是先辈?

两个序列,例如:\(A = [1, 2, 3, 4, 5]\)\(B = [8, 8, 8, 8]\),那么这就是个先辈。

因为后面那一串数比前面的那一串都大(\(8\)\(1\)\(2\)\(3\)\(4\)\(5\)大),因此\(A\)序列比\(B\)序列屑。

简单来说,就是需要查找一个序列是否是不降序列。

为什么要用线段树?

看到区间操作就明白了啊(不是因为我不太会树状数组……)

解题思路

可能正解第一时间没办法想出来,因为题目中有着判断线段树上不降的序列。

但是,我们可以通过差分数组来帮助我们解决这个问题。

为什么会想到差分?

因为我们知道差分数组也是可以进行快速区间加减的(就是差分数组的原理)。

那么这个题目,因为我们需要查找一个不降的序列,那么我们就可以转化为:

找到线段树上差分数组\(\geq 0\)的这一段序列

因此,我们建树就使用原序列的差分数组。

既然知道了用差分,那么就是用线段树单点修改\(+\)区间询问的模板啦。

如果不会差分怎么办?

答:Google it。

需要的数组及变量

a[N]数组:存储原序列。

d[N]数组:差分数组(一般情况差分数组都用d[]表示)。

sum[N]数组:线段树。

oplrxnm:题目所给。

设计代码

read()快读函数(手动快读)

不要问为什么要用快读……

普通std::cinstd::cout会挂。

加速std::cinstd::cout同样会挂。

scanf()printf()不会挂。

read()print()不会挂,并且在第35个点会比用scanf()printf()快将近一倍。

因此,还是加一个吧(不要像我之前卡在1.00s然后只得了\(60\)分的好成绩)。

int read(){
    int x = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + (ch - '0');
        ch = getchar();
    }
    return x * w;
}

push_up()函数

这里的push_up()函数需要注意,因为我们的差分存储的是两数的差值,而我们需要查询的那一段区间是要不降的,因此这一段区间内,两个数之间的差值最小只能是0。

因此,我们维护的是一个\(min\)值,只要这一段区间的\(min \geq 0\),那么就说明这一段区间是先辈(我们想要找的不降序列)。

void push_up(int root) {
    sum[root] = min(sum[lroot], sum[rroot]);
}

build()函数

我们在上面↑的解题思路里面讲过,我们建树是以原序列的差分数组进行建树,所以这个函数就是模板里面的建树函数。

void build(int l, int r, int root){
    if (l == r) {
		sum[root] = a[l] - a[l - 1];
		return;
	}
    register int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(root);
    return;
}

update()函数

我们使用的是差分函数,而差分函数可以通过直接对一段区间的两头进行修改就能完成对区间修改的操作。

因此我们的update()函数不再是直接对一段区间进行修改(或者是使用Lazy Tag),而是直接对区间的两端点进行修改。

因此,这个update()函数就是线段树模板里的单点修改模板了。

void update(int point, int newval, int l, int r, int root) {
    if (l == r) {
		sum[root] += newval;
		return;
	}
    int mid = (l + r) >> 1;
    if (point <= mid)
		update(point, newval, lson);
    else
		update(point, newval, rson);
    push_up(root);
}

query()函数

既然我们查询的是最小值,那么就直接套用模板吧。

int query(int ledge, int redge, int l, int r, int root) {
    if (ledge <= l && r <= redge)
		return sum[root];
    int mid = (l + r) >> 1;
    int ans = inf;
    if (ledge <= mid)
		ans = min(ans, query(ledge, redge, lson));
    if (redge > mid)
		ans = min(ans, query(ledge, redge, rson));
    return ans;
}

main()主函数

这就不用多说了吧,主函数就是正常的线段树模板主函数。

只不过在update()函数那个地方记得使用差分的区间修改操作哦(别忘了我们是在用差分数组,区间修改就是加减左端点减加右端点)。

n = read(); m = read();
for (int i = 1; i <= n; i++)
	a[i] = read();
build(1, n, 1);
for (int i = 1; i <= m; i++) {
    op = read();
    if (op == 1) {
        l = read(); r = read(); x = read();
        update(l, x, 1, n, 1);
        update(r + 1, -x, 1, n, 1);
    } 
		else if (op == 2) {
			l = read(); r = read();
			int ok = query(l + 1, r, 1, n, 1);
        if (ok < 0)
        	printf("No\n");
        else
        	printf("Yes\n");
    }
} 

完整代码

#include <bits/stdc++.h>
#define lson l, mid, root << 1
#define rson mid + 1, r, root << 1 | 1
#define lroot root << 1
#define rroot root << 1 | 1
#define int long long
#define inf 0x7f7f7f7f7f7f7f
using namespace std;
const int N = 1e6 + 10;
int a[N], d[N], sum[N << 2];
int op, l, r, x, n, m;
int read(){
    int x = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + (ch - '0');
        ch = getchar();
    }
    return x * w;
}
void push_up(int root) {
    sum[root] = min(sum[lroot], sum[rroot]);
}
void build(int l, int r, int root){
    if (l == r) {
		sum[root] = a[l] - a[l - 1];
		return;
	}
    register int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(root);
    return;
}
void update(int point, int newval, int l, int r, int root) {
    if (l == r) {
		sum[root] += newval;
		return;
	}
    int mid = (l + r) >> 1;
    if (point <= mid)
		update(point, newval, lson);
    else
		update(point, newval, rson);
    push_up(root);
}
int query(int ledge, int redge, int l, int r, int root) {
    if (ledge <= l && r <= redge)
		return sum[root];
    int mid = (l + r) >> 1;
    int ans = inf;
    if (ledge <= mid)
		ans = min(ans, query(ledge, redge, lson));
    if (redge > mid)
		ans = min(ans, query(ledge, redge, rson));
    return ans;
}
signed main() {	
    n = read(); m = read();
    for (int i = 1; i <= n; i++)
    	a[i] = read();
    build(1, n, 1);
    for (int i = 1; i <= m; i++) {
        op = read();
        if (op == 1) {
            l = read(); r = read(); x = read();
            update(l, x, 1, n, 1);
            update(r + 1, -x, 1, n, 1);
        } 
		else if (op == 2) {
			l = read(); r = read();
			int ok = query(l + 1, r, 1, n, 1);
            if (ok < 0)
            	printf("No\n");
            else
            	printf("Yes\n");
        }
    }
    return 0;
}

给个赞吧!

posted @ 2023-02-07 20:21  煎饼Li  阅读(9)  评论(0编辑  收藏  举报