zoukankan      html  css  js  c++  java
  • UOJ228 简单数据结构练习题

    Description

    传送门
    维护一个数列, 有以下操作:

    1. 对[l,r]同时加上x

    2. 把[l,r]开根后下取整.

    3. 查询[l,r]之和

    n,m (leq)$ 100000, $$a_i,x leq 10^5$

    Solution

    考虑一个简易的线段树,直接对一个区间进行开根. 如果这个区间数字不同就继续往下递归. 因为 开根开不了多少次就会变成1,所以复杂度相对较小.但考虑一种情况:8 9 8 9 8 9 (dots) 这样变成1后再区间修改变成原来的样子.然后就会TLE.

    怎么办呢. 考虑一个小小优化,如果(Max - sqrt{Max} = Min - sqrt{Min}), 即区间变化量相同, 就直接转换为一个减法.

    然后就跑过去了, 还跑得飞快.

    分析一下复杂度:

    对于一个数(y =a^{2^{k}}), 我们对他开根是:(k)次的, 即(log_{2}({log_{a}(y)})). 对于一个数, 我们在线段树上跳是(log{n})次的.

    我们定义势能V为相邻两数的差. 那么越开根号,V就越小.

    显然,我们一次加法最多让V增加到最大值, 总势能为mV, 所以复杂度为(O((n + m)logN * loglogV))

    Codes

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
    #define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
    #define clar(a, b) memset((a), (b), sizeof(a))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    #define Debug(s) debug("The massage in line %d, Function %s: %s
    ", __LINE__, __FUNCTION__, s)
    typedef long long LL;
    typedef long double LD;
    const int BUF_SIZE = (int)1e6 + 10;
    struct fastIO {
        char buf[BUF_SIZE], buf1[BUF_SIZE];
        int cur, cur1;
        FILE *in, *out;
        fastIO() {
            cur = BUF_SIZE, in = stdin, out = stdout;
    		cur1 = 0;
        }
        inline char getchar() {
            if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
            return *(buf + (cur++));
        }
        inline void putchar(char ch) {
            *(buf1 + (cur1++)) = ch;
            if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
        }
        inline int flush() {
            if (cur1 > 0) fwrite(buf1, cur1, 1, out);
            return cur1 = 0;
        }
    }IO;
    #define getchar IO.getchar
    #define putchar IO.putchar
    int read() {
    	char ch = getchar();
    	int x = 0, flag = 1;
    	for(;!isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
    	for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    	return x * flag;
    }
    void write(LL x) {
    	if(x < 0) putchar('-'), x = -x;
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + 48);
    }
    void putString(char s[], char EndChar = '
    ') {
    	rep(i, 0, strlen(s) - 1) putchar(*(s + i));
    	if(~EndChar) putchar(EndChar);
    }
    
    #define Maxn 100009
    int n, m, a[Maxn];
    namespace SGMT_tree {
    	LL Max[Maxn << 2], Min[Maxn << 2], sum[Maxn << 2], tag[Maxn << 2];
    #define lc(x) ((x) << 1)
    #define rc(x) (((x) << 1) | 1)
    	void pushup(int root) {
    		Max[root] = max(Max[lc(root)], Max[rc(root)]);
    		Min[root] = min(Min[lc(root)], Min[rc(root)]);
    		sum[root] = sum[lc(root)] + sum[rc(root)];
    	}
    	void pushdown(int root, int l, int r) {
    		LL v = tag[root];
    		if(v) {
    			int mid = (l + r) >> 1;
    			Max[lc(root)] += v, Min[lc(root)] += v, tag[lc(root)] += v; sum[lc(root)] += 1ll * v * (mid - l + 1);
    			Max[rc(root)] += v, Min[rc(root)] += v, tag[rc(root)] += v; sum[rc(root)] += 1ll * v * (r - mid);
    			tag[root] = 0;
    		}
    	}
    	void build(int root, int l, int r) {
    		if(l == r) {
    			Max[root] = Min[root] = sum[root] = a[l];
    			tag[root] = 0;
    			return ;
    		}
    		int mid = (l + r) >> 1;
    		build(lc(root), l, mid);
    		build(rc(root), mid + 1, r);
    		pushup(root);
    	}
    	void modify(int root, int l, int r, int x, int y, LL v) {
    		if(l > r || r < x || l > y) return ;
    		if(x <= l && r <= y) {
    			Max[root] += v, Min[root] += v, tag[root] += v;
    			sum[root] += 1ll * v * (1ll * r - l + 1);
    			return;
    		}
    		int mid = (l + r) >> 1;
    		pushdown(root, l, r);
    		modify(lc(root), l, mid, x, y, v);
    		modify(rc(root), mid + 1, r, x, y, v);
    		pushup(root);
    	}
    	void modify(int root, int l, int r, int x, int y) {
    		if(l > r || r < x || l > y) return ;
    		if(x <= l && r <= y) {
    			if(Max[root] - ((int)sqrt(Max[root])) == Min[root] - ((int)sqrt(Min[root]))) {
    				LL v = -Max[root] + ((int)sqrt(Max[root]));
    				Max[root] += v, Min[root] += v, tag[root] += v, sum[root] += 1ll * v * (r - l + 1);
    				return ;
    			}
    		}
    		int mid = (l + r) >> 1;
    		pushdown(root, l, r);
    		modify(lc(root), l, mid, x, y);
    		modify(rc(root), mid + 1, r, x, y);
    		pushup(root);
    	}
    	LL query(int root, int l, int r, int x, int y) {
    		if(l > r || r < x || l > y) return 0;
    		if(x <= l && r <= y) return sum[root];
    		int mid = (l + r) >> 1; LL res = 0;
    		pushdown(root, l, r);
    		res += query(lc(root), l, mid, x, y);
    		res += query(rc(root), mid + 1, r, x, y);
    		pushup(root);
    		return res;
    	}
    }
    namespace INIT {
    	void Main() {
    		n = read();	m = read();
    		rep(i, 1, n) a[i] = read();
    		SGMT_tree :: build(1, 1, n);
    	}
    }
    namespace SOLVE {
    	void Main() {
    		rep(i, 1, m) {
    			int opt = read();
    			if(opt == 1) {
    				int l = read(), r = read(), x = read();
    				SGMT_tree :: modify(1, 1, n, l, r, x);
    			}
    			if(opt == 2) {
    				int l = read(), r = read();
    				SGMT_tree :: modify(1, 1, n, l, r);
    			}
    			if(opt == 3) {
    				int l = read(), r = read();
    				write(SGMT_tree :: query(1, 1, n, l, r)), putchar('
    ');
    			}
    		}
    	}
    }
    int main() {
    #ifdef Qrsikno
    	freopen("uoj228.in", "r", stdin);
    	freopen("uoj228.out", "w", stdout);
    #endif
    	INIT :: Main();
    	SOLVE :: Main();
    #ifdef Qrsikno
    	debug("
    Running time: %.3lf(s)
    ", clock() * 1.0 / CLOCKS_PER_SEC);
    #endif
    	return IO.flush();
    }
    
  • 相关阅读:
    【递归】拆分自然数
    HDOJ3068最长回文
    博弈论——尼姆博奕
    vijos P1047最小公倍数
    Eular质数筛法-hiho一下 第九十三周
    hdoj-5652 India and China Origins二分+bfs
    hdoj-1166排兵布阵 简单的树状数组
    hdoj-5641 king's phone
    hdoj-1548简单的bfs题目
    命令中"|"的意义
  • 原文地址:https://www.cnblogs.com/qrsikno/p/9791341.html
Copyright © 2011-2022 走看看