zoukankan      html  css  js  c++  java
  • luoguP5105 不强制在线的动态快速排序 [官方?]题解 线段树 / set

    不强制在线的动态快速排序 题解


    算法一

    按照题意模拟

    维护一个数组,每次直接往数组后面依次添加([l, r])

    每次查询时,暴力地(sort)查询即可

    复杂度(O(10^9 * q)),期望得分(0)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int n, opt;
    int q[.........], to;
    
    int main() {
    	cin >> n;
    	for(int i = 1; i <= n; i ++) {
    		cin >> opt;
    		if(opt == 1) {
    			int l, r;
    			cin >> l >> r;
    			for(int j = l; j <= r; j ++) q[++ to] = j;
    			sort(q + 1, q + to + 1);
    			to = unique(q + 1, q + to + 1) - q - 1;
    		}
    		else {
    			long long ans = 0;
    			for(int i = 2; i <= to; i ++)
    				ans ^= (1ll * q[i] * q[i] - 1ll * q[i - 1] * q[i - 1]);
    			printf("%lld
    ", ans);
    		}
    	}
    	return 0;
    }
    
    

    算法二

    (igoplus limits_{i = 2}^n (a_i^2 - a_{i - 1}^2) = igoplus limits_{i = 2}^n (a_i + a_{i - 1})(a_i - a_{i - 1}))

    (a_i = a_{i - 1})时,可以发现((a_i, a_{i - 1}))是对(V(S))没有贡献的,也就是说,重复的数等价于一个

    那么,我们不用数组了

    我们用自带去重的(set)来模拟插入

    查询时暴力即可

    期望得分(0)


    算法三

    插入单点看起来很可做

    用一个(set)来维护

    每次插入时,对前驱后继来讨论即可

    插入复杂度(O(log n)),询问复杂度(O(1))

    期望得分(0 sim 20)


    算法四

    插入的是一段值域区间

    因此不妨以区间为单位来考虑

    区间([l, r])形成的贡献为(igoplus limits_{i = 2}^n (a_i - a_{i - 1})* (a_i + a_{i - 1})),由于(a_i = a_{i - 1} + 1)

    因此,区间([l, r])形成的贡献为(igoplus limits_{i = 1}^{n - 1} 2 a_i + 1 = igoplus limits_{i = l}^{r -1} 2i + 1 = igoplus limits_{i = 1}^{r - 1} 2i + 1 oplus igoplus limits_{i = 1}^{l - 1} 2i + 1)


    考虑怎么求(igoplus limits_{i = 1}^n 2i + 1)

    • 这个问题等价于问([1, 2n + 1])中的所有奇数的异或和
      按位分解之后,只要支持区间([1, n])中有多少个(2^i)即可
      再来个大力数位(dp)
      复杂度(O(log^2 n))

    • 询问区间([1, n])中有多少个(2^i)可以(O(1))回答
      对于每一段(2^{i + 1})而言,前(2^i)的第(i)位为(0),后(2^i)的第(i)位为(1)
      复杂度(O(log n))

    • 打表,可以发现(O(1))的规律
      (n = 4m + k)
      (k = {0, 1, 2, 3})时,前缀异或和依次为(2n, 3, 2n + 2, 1)
      可以考虑用归纳法证明
      复杂度(O(1))

    在下文中,我们取(f(n))表示计算异或前缀和的复杂度


    可以考虑每次暴力的维护区间的并(用(sort)来维护...)

    期望得分(30 sim 40)


    算法五

    考虑用(set)来维护区间

    像珂朵莉树那样去维护大概就行了

    复杂度(O(n log n + n * f(n)))

    期望得分(50 sim 100)


    算法六

    考虑用值域线段树来维护

    在线段树上的每个节点维护(min, max, sum)即可更新

    需要动态开点

    每次插入对应着给一些区间打标记

    每次查询可以直接取(sum[root])即可

    注意一个区间被打了标记之后,它的所有子区间都不需要打标记了

    复杂度(O(n f(n) log 10^9))

    期望得分(50 sim 100)

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    #define ll long long
    #define ri register int
    #define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
    #define drep(io, ed, st) for(ri io = ed; io >= st; io --)
    
    #define gc getchar
    inline int read() {
        int p = 0, w = 1; char c = gc();
        while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
        while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
        return p * w;
    }
        
    const int sid = 1e7 + 5;
    
    ll sum[sid];
    bool tag[sid];
    int m, rt, id;
    int mi[sid], mx[sid], ls[sid], rs[sid];
    
    inline void upd(int o) {
        int lc = ls[o], rc = rs[o];
        mi[o] = mi[lc] ? mi[lc] : mi[rc];
        mx[o] = mx[rc] ? mx[rc] : mx[lc];
        sum[o] = sum[lc] ^ sum[rc];
        if(mx[lc] && mi[rc]) sum[o] ^= 1ll * (mi[rc] + mx[lc]) * (mi[rc] - mx[lc]);
        if(tag[lc] && tag[rc]) tag[o] = 1;
    }
    
    inline int pre(int x) {
        int o = x & 3;
        if(o == 0) return (x << 1);
        if(o == 1) return 3;
        if(o == 2) return (x << 1) + 2;
        if(o == 3) return 1;
    }
    
    void mdf(int &o, int l, int r, int ml, int mr) {
        if(ml > r || mr < l || tag[o]) return;
        if(!o) o = ++ id;
        if(ml <= l && mr >= r) { 
            mi[o] = l; mx[o] = r;
            sum[o] = pre(r - 1) ^ pre(l - 1);
            tag[o] = 1;
            return; 
        }
        int mid = (l + r) >> 1;
        mdf(ls[o], l, mid, ml, mr);
        mdf(rs[o], mid + 1, r, ml, mr);
        upd(o);
    }
    
    int main() {
    
        m = read();
        rep(i, 1, m) {
            int opt = read();
            if(opt == 1) {
                int l = read(), r = read();
                mdf(rt, 1, 1e9, l, r);
            }
            else if(opt == 2)
                printf("%lld
    ", sum[1]);
        }
        
        return 0;
    }
    

    这是一道我也不知道区分了什么反正挺水的题

    好像忘了给不会算区间异或和的分了

    不管了,反正大家都能A嘛


    一些诡异的东西

    (oplus 2i + 1),其实等价于((oplus i) * 2 + (i ;&;1))

    然后只要考虑((oplus i))即可

    这个就好考虑多了

  • 相关阅读:
    Codeforces Round #536 E. Lunar New Year and Red Envelopes /// 贪心 记忆化搜索 multiset取最大项
    牛客网暑期ACM多校训练营(第五场) F
    关于线段树or 树状树状 在二维平面搞事情!Orz
    ZOJ 3822 ( 2014牡丹江区域赛D题) (概率dp)
    HDU4336 Card Collector (概率dp+状压dp)
    POJ 2151 Check the difficulty of problems(概率DP)
    CF E2
    HDU4089 Activation(概率DP+处理环迭代式子)
    HDU4035 Maze (概率DP)
    牛客 Rabbit的数列 (线段树维护值为x的个数+区间覆盖)
  • 原文地址:https://www.cnblogs.com/reverymoon/p/10091185.html
Copyright © 2011-2022 走看看