【挖坑】【GSS系列】GSS4:区间开平方

版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/88657453

Can you answer these queries?

GSS系列是spoj出品的一套数据结构好毒瘤题,主要以线段树、平衡树和树链剖分为背景,进行了一些操作的魔改,使得难度远超模板题,但对于思维有极大的提升。

所以我会选择一些在我能力范围内的题挖坑选讲,构成一个GSS系列。至于剩下那些,等我成为巨佬弄懂了再说吧。

GSS4:区间开平方

这道题可能是GSS系列中的一个另类吧,因为它并没有涉及到最大子段和,而且难度也不是特别大,是一个经典的套路题。但其精妙之处就在于数据结构与暴力的完美结合。

原题传送门(洛谷)

题意

给定一段区间,要求支持区间开平方(下取整)和查询区间和。

口胡

这题真的没什么可以图解的

本题最大的问题在于区间开方后不能在 O ( 1 ) O(1) 时间内维护区间和。但是不管你用什么办法,你永远无法把区间开平方和区间和联系起来。所以此题需要另辟蹊径。

我们会发现,如果对一个数不断开方,那么这个数字会很快变小,最后变为1,然后不管你怎么开方,都不会再改变。对于题目中的数据,大概对一个数开六到七次平方,就会变成1。于是我们可以想到,对于每次开平方操作,我们都暴力单点修改区间,如果修改后整段区间都变为了1,就在线段树上打标记,下次修改时直接跳过此区间,而区间和直接用线段树维护即可。复杂度仍旧可以看做 O ( n l o g n ) O(nlogn) ,只不过有比较大的常数。

具体实现中,如何判断整个区间为1是最重要的。主要有两种实现方式:

  1. 线段树同时维护区间最大值,如果区间最大值已经为1,那么整个区间一定均小于等于1(可能为0)。
  2. 线段树每个节点增加一个 t a g tag ,表示这段区间是否为1,一个节点的tag在push_up时由两个子节点进行与运算得到,只有两个子节点均为1时,父亲节点才为1.

代码

区间最大值实现

#include <bits/stdc++.h>
#define reset(x) memset(x, 0, sizeof(x))
#define lc(x) (x<<1)
#define rc(x) (x<<1|1)
#define mid (l+r>>1)
#define ll long long
#define MAX 100005
using namespace std;

int n, m, t;
ll a[MAX], sum[MAX*4], ma[MAX*4];

void push_up(int p) {
    sum[p] = sum[lc(p)]+sum[rc(p)];
    ma[p] = max(ma[lc(p)], ma[rc(p)]);
}

void build(int p, int l, int r) {
    if(l == r) {
        sum[p] = ma[p] = a[l];
        return;
    }
    build(lc(p), l, mid);
    build(rc(p), mid+1, r);
    push_up(p);
}

void update(int p, int l, int r, int ul, int ur) {
    if(l>=ul && r<=ur && ma[p] <= 1) {
        return;
    }
    if(l == r) {
        sum[p] = ma[p] = (ll)sqrt(sum[p]);
        return;
    }
    if(mid >= ul) {
        update(lc(p), l, mid, ul, ur);
    }
    if(mid < ur) {
        update(rc(p), mid+1, r, ul, ur);
    }
    push_up(p);
}

ll query(int p, int l, int r, int ul, int ur) {
    if(l>=ul && r<=ur) {
        return sum[p];
    }
    ll res = 0;
    if(mid >= ul) {
        res += query(lc(p), l, mid, ul, ur);
    }
    if(mid < ur) {
        res += query(rc(p), mid+1, r, ul, ur);
    }
    return res;
}

void solve() {
    reset(ma);
    reset(a);
    reset(sum);
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    if(t>1) puts("");
    printf("Case #%d:\n", t);
    build(1, 1, n);
    cin >> m;
    int t, x, y;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &t, &x, &y);
        if(x > y)
            swap(x, y);
        if(t == 1) {
            printf("%lld\n", query(1, 1, n, x, y));
        } else {
            update(1, 1, n, x, y);
        }
    }
}

int main() {
    while(cin >> n){
        t++;
        solve();
    }

    return 0;
}

tag实现

#include <bits/stdc++.h>
#define MAX 100005
#define ll long long
#define lc(x) (x<<1)
#define rc(x) (x<<1|1)
#define mid ((l+r)>>1)
using namespace std;

template <typename T>
inline void read(T &n){
	n = 0;
	char c = getchar();
	while(!isdigit(c) && c != '-') c = getchar();
	int f = 1;
	if(c == '-') f = -1, c = getchar();
	while(isdigit(c)) n = (n<<3)+(n<<1)+c-'0', c = getchar();
	n *= f;
}

template <typename T>
void write(T n){
	if(n < 0) putchar('-'), n = -n;
	if(n>9) write(n/10);
	putchar(n%10+'0');
}

int n, m;
ll a[MAX], s[MAX*4], tag[MAX*4];

inline void push_up(int p){
	s[p] = s[lc(p)]+s[rc(p)];
	tag[p] = (tag[lc(p)] & tag[rc(p)]);
}

void build(int p, int l, int r){
	if(l == r){
		s[p] = a[l];
		if(a[l] <= 1) tag[p] = 1;
		return;
	}
	build(lc(p), l, mid);
	build(rc(p), mid+1, r);
	push_up(p);
}

void update(int p, int l, int r, int ul, int ur){
	if(tag[p]) return;
	if(l == r){
		s[p] = (int)sqrt(s[p]);
		if(s[p] <= 1) tag[p] = 1;
		return;
	}
	if(mid >= ul) update(lc(p), l, mid, ul, ur);
	if(mid < ur) update(rc(p), mid+1, r, ul, ur);
	push_up(p);
}

ll query(int p, int l, int r, int ul, int ur){
	if(l >= ul && r <= ur){
		return s[p];
	}
	ll res = 0;
	if(mid >= ul) res += query(lc(p), l, mid, ul, ur);
	if(mid < ur) res += query(rc(p), mid+1, r, ul, ur);
	return res;
}

int main()
{
	read(n);
	for(int i = 1; i <= n; i++){
		read(a[i]);
	}
	build(1, 1, n);
	read(m);
	int t, l, r;
	for(int i = 1; i <= m; i++){
		read(t), read(l), read(r);
		if(l>r) swap(l, r);
		if(t == 1){
			write(query(1,1,n, l, r));
			puts("");
		}
		else{
			update(1,1,n, l, r);
		}
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/88657453