zoukankan      html  css  js  c++  java
  • 分块详解

    hzwer的9题
    https://loj.ac/problem/6277
    https://loj.ac/problem/6278
    https://loj.ac/problem/6279
    https://loj.ac/problem/6280
    https://loj.ac/problem/6281
    https://loj.ac/problem/6282
    https://loj.ac/problem/6283
    https://loj.ac/problem/6284
    https://loj.ac/problem/6285
    

    例题会稍后放上

    分块是一种优雅的暴力

    大概思想是这样的:维护(sqrt(n))个块 查询的时候是查询区间内的块 如果有的部分不满一个块就用枚举 常数较小

    对于上面这个说明 我举个简单的例子: 假设n = 10000 那么 每个块就是(sqrt n) = 100 每个块是

    1~100
    101~200
    ...
    9901~10000
    

    假设要查询 2 ~ 999的值

    你只需要维护每个 大小 为 (sqrt n) 的块 也就是 大小 为 100 的块 然后对(101)~(200) - (801)~(900)这几个块查询块的值
    然后暴力 (2)~(100)(901) ~ (999) 的值

    之所以称为优雅的暴力 分块把朴素暴力的 (2) ~ (999) 改成了 (200) 复杂度左右的暴力

    个人觉得 (2)~(999)是最坏情况

    如果查询 (1)~(1000) 那么分块的优势就出来了

    分块的预处理是 将每一个数字存入一个块中 复杂度是( heta n)

    然后区间修改查询什么的 最多不超过 (3sqrt n)

    证明:一个块的大小是(sqrt(n)) 那么多出来的左区间和右区间的最坏情况是 (sqrt(n))) 所以易证

    分块的时间复杂度大概就是 ( heta (N + q * sqrt(N)))
    (n指序列长度 q指查询修改的操作次数)
    以上就是一个基本的分块思想 简单讲 分块比线段树容易实现

    ( ext{分块1})
    分块1是区间修改 单点查询

    我们用一个数组维护块

    如果区间修改的时候 包含这个块 那么可以把这个块加上需要修改的值 如果是多出来的就直接暴力修改了

    然后查询的时候输出所在块修改的值 和 本身的值

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    using namespace std ;
    const int N = 50000 + 5 ;
    const int Bl = 300 + 5 ;
    struct node {
        int l , r ;
        int add ;
    } ;
    int n ;
    int a[N] ;
    node atag[Bl] ;
    int bl[N] ; int unt ;
    inline void change(int l , int r , int c) {
        for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++)
            a[i] += c ;
        if(bl[l] != bl[r])
            for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++)
                a[i] += c ;
        for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++)
            atag[i].add += c ;
    }
    signed main() {
        ios::sync_with_stdio(false) ;
        cin >> n ;
        for(register int i = 1 ; i <= n ; i ++) cin >> a[i] ;
        unt = sqrt(n) ;
        for(register int i = 1 ; i <= n ; i ++) {
            bl[i] = (i - 1) / unt + 1 ;
        }
        for(register int i = 1 ; i <= n ; i ++) {
            int opt ;
            cin >> opt ;
            if(opt == 0) {
                int l , r , c ;
                cin >> l >> r >> c ;
                change(l , r , c) ;
            }
            else {
                int l , r , c ;
                cin >> l >> r >> c ;
                cout << a[r] + atag[bl[r]].add << endl ;
            }
        }
        return 0 ;
    }
    

    ( ext{分块2})

    分块二求的是区间修改 区间查询小于(c^2)的最大数
    vector 每次修改完重构一下左右块就行了

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    inline int read() {
        register int x = 0;
        register int f = 1;
        register char c;
    #define gc c = getchar()
        while (isspace(gc))
            ;
        c == '-' ? gc, f = -1 : 0;
        while (x = (x << 3) + (x << 1) + (c & 15), isdigit(gc))
            ;
        return x * f;
    }
    const int N = 50000 + 5;
    const int Bl = 300 + 5;
    int n;
    int a[N];
    struct node {
        int add;
        std::vector<int> v;
    };
    node atag[Bl];
    int unt;
    int bl[N];
    inline void reset(int x) {
        atag[x].v.clear();
        for (register int i = (x - 1) * unt + 1; i <= min(x * unt, n); i++) atag[x].v.push_back(a[i]);
        sort(atag[x].v.begin(), atag[x].v.end());
        return;
    }
    inline void change(int l, int r, int c) {
        for (register int i = l; i <= min(bl[l] * unt, r); i++) a[i] += c;
        reset(bl[l]);
        if (bl[l] != bl[r]) {
            for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) a[i] += c;
            reset(bl[r]);
        }
        for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) atag[i].add += c;
    }
    inline int query(int l, int r, LL c) {
        int ans = 0;
        for (register int i = l; i <= min(bl[l] * unt, r); i++)
            if (a[i] + atag[bl[l]].add < c)
                ans++;
        if (bl[l] != bl[r]) {
            for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++)
                if (a[i] + atag[bl[r]].add < c)
                    ans++;
        }
        for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) {
            int s = c - atag[i].add;
            ans += lower_bound(atag[i].v.begin(), atag[i].v.end(), s) - atag[i].v.begin();
        }
        return ans;
    }
    signed main() {
        n = read();
        unt = sqrt(n);
        for (register int i = 1; i <= n; i++) a[i] = read();
        for (register int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1;
        for (register int i = 1; i <= n; i++) {
            atag[bl[i]].v.push_back(a[i]);
        }
        for (register int i = 1; i <= bl[n]; i++) {
            sort(atag[i].v.begin(), atag[i].v.end());
        }
        for (register int i = 1; i <= n; i++) {
            int opt = read(), L = read(), R = read(), c = read();
            if (opt)
                printf("%lld
    ", query(L, R, c * c));
            else
                change(L, R, c);
        }
        return 0;
    }
    

    ( ext{分块3})

    分块3 区间修改 询问区间内小于某个值 x的前驱即比x小的最大数

    同样使用一个(multiset)维护(请仔细思考为什么不能用set)
    然后用(lowerbound)二分
    (部分C++11内容

    #pragma GCC diagnostic error "-std=c++11"
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    inline int read() {
        register int x = 0;
        register int f = 1;
        register char c;
    #define gc c = getchar()
        while (isspace(gc))
            ;
        c == '-' ? gc, f = -1 : 0;
        while (x = (x << 3) + (x << 1) + (c & 15), isdigit(gc))
            ;
        return x * f;
    }
    const int N = 100000 + 5;
    const int Bl = 400 + 5;
    int n;
    int a[N];
    struct node {
        int add;
    };
    node atag[Bl];
    int unt;
    int bl[N];
    multiset<int> st[Bl];
    inline void change(int l, int r, int c) {
        for (register int i = l; i <= min(bl[l] * unt, r); i++) {
            st[bl[i]].erase(a[i]);
            a[i] += c;
            st[bl[i]].insert(a[i]);
        }
        if (bl[l] != bl[r]) {
            for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) {
                st[bl[i]].erase(a[i]);
                a[i] += c;
                st[bl[i]].insert(a[i]);
            }
        }
        for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) atag[i].add += c;
    }
    inline int query(int l, int r, int c) {
        int ans = INT_MIN;
        for (register int i = l; i <= min(bl[l] * unt, r); i++)
            if (a[i] + atag[bl[l]].add < c)
                ans = max(a[i] + atag[bl[l]].add, ans);
        if (bl[l] != bl[r]) {
            for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++)
                if (a[i] + atag[bl[r]].add < c)
                    ans = max(a[i] + atag[bl[r]].add, ans);
        }
        for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) {
            int s = c - atag[i].add;
            auto find = st[i].lower_bound(s);
            if (find == st[i].begin())
                continue;
            find--;
            ans = max(ans, *find + atag[i].add);
        }
        return ans == INT_MIN ? -1 : ans;
    }
    signed main() {
        n = read();
        unt = 500 + 5;
        for (register int i = 1; i <= n; i++) a[i] = read();
        for (register int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1;
        for (register int i = 1; i <= n; i++) {
            st[bl[i]].insert(a[i]);
        }
        for (register int i = 1; i <= n; i++) {
            int opt = read(), L = read(), R = read(), c = read();
            if (opt)
                printf("%d
    ", query(L, R, c));
            else
                change(L, R, c);
        }
        return 0;
    }
    
    

    ({ ext{分块4}})

    分块4是区间修改 区间求和
    然后 区间和 % (c+1)

    我们考虑使用一个数组维护区间和 然后用一个数组维护整个块的修改情况

    维护区间和指的是 暴力修改 的部分

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    
    using namespace std ;
    #define int long long
    inline int read() { register int x = 0 ; register int f = 1 ; register char c ;
    #define gc c = getchar()
    	while(isspace(gc)) ;
    	c == '-' ? gc , f = -1 : 0 ;
    	while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ;
    	return x * f ;
    }
    
    int n ;
    const int N = 50000 + 5 ;
    int a[N] ;
    int bl[N] ;
    int sum[N] ;
    int atag[N] ;
    int unt ;
    inline void change(int l , int r , int c) {
    	for(register int i = l ; i <= min(r , bl[l] * unt) ; i ++) {
    		a[i] += c ;
    		sum[bl[l]] += c ;
    	}
    	if(bl[l] != bl[r])
    		for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) {
    			a[i] += c ;
    			sum[bl[r]] += c ;
    		}
    	for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) atag[i] += c ;
    	return ;
    }
    inline int query(int l , int r , int c) { int ans = 0 ;
    	for(register int i = l ; i <= min(r , bl[l] * unt) ; i ++) {
    		ans += a[i] + atag[bl[l]] ;
    		ans %= c ;
    	}
    	if(bl[l] != bl[r])
    		for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) {
    			ans += a[i] + atag[bl[r]] ;
    			ans %= c ;
    		}
    	for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++)
    		ans = (ans + sum[i] + atag[i] * unt) % c ;
    	return ans ;
    }
    signed main() {
    	n = read() ; unt = sqrt(n) ;
    	for(register int i = 1 ; i <= n ; i ++) a[i] = read() ;
    	for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
    	for(register int i = 1 ; i <= n ; i ++) {
    		sum[bl[i]] += a[i] ;
    	}
    	for(register int i = 1 ;i <= n ; i ++) {
    		int opt = read() ;
    		if(opt == 0) {
    			int l = read() , r = read() , c = read() ;
    			change(l , r , c) ;
    		}
    		if(opt == 1) {
    			int l = read() , r = read() , c = read() ;
    			printf("%lld
    " , query(l , r , c + 1)) ;
    		}
    	}
    	return 0 ;
    }
    

    ( ext{分块5})

    分块5是区间开方 然后区间查询
    对于每个块 大小最大是(2^{31})

    (log(31) ≈ 5)
    所以可以得出一个块最多开方6次
    也就是最大是(6n)
    所以对于区间开方 对一个块进行开方 然后重构这个块的总和

    也就是可以得出 大块修改并用一个flg数组标记这个块有没有大于1的数字(如果大于1的话还可以开方
    这样可以避免很多次重复开方
    那么对于没有完整块的左右区间 我们考虑先减掉原数字然后加上开方后的数字(对于乘法操作也是一样的
    区间查询上面讲过了(将块的总和加上 然后暴力把左右区间求和)

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    
    using namespace std ;
    #define int long long
    inline int read() { register int x = 0 ; register int f = 1 ; register char c ;
    #define gc c = getchar()
    	while(isspace(gc)) ;
    	c == '-' ? gc , f = -1 : 0 ;
    	while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ;
    	return x * f ;
    }
    
    int n ;
    const int N = 50000 + 5 ;
    int a[N] ;
    int bl[N] ;
    int flg[N] ;
    int sum[N] ;
    int unt ;
    inline void Reset(int x) {
        if(flg[x]) return ;
        flg[x] = 1 ;
        sum[x] = 0 ;
        for(register int i = (x - 1) * unt + 1 ; i <= x * unt ; i ++) {
            a[i] = sqrt(a[i]) ;
            sum[x] += a[i] ;
            if(a[i] > 1) flg[x] = 0 ;
        }
        return ;
    }
    inline void change(int l , int r , int c) {
        for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) {
            sum[bl[l]] -= a[i] ;
            a[i] = sqrt(a[i]) ;
            sum[bl[l]] += a[i] ;
        }
        if(bl[l] != bl[r])
            for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) {
                sum[bl[r]] -= a[i] ;
                a[i] = sqrt(a[i]) ;
                sum[bl[r]] += a[i] ;
            }
        for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) Reset(i) ;
        return ;
    }
    inline int query(int l , int r , int c) { int ans = 0 ;
        for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) {
            ans += a[i] ;
        }
        if(bl[l] != bl[r])
            for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) ans += a[i] ;
        for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) ans += sum[i] ;
        return ans ;
    }
    signed main() {
        n = read() ; unt = sqrt(n) ;
        for(register int i = 1 ; i <= n ; i ++) a[i] = read() ;
        for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
        for(register int i = 1 ; i <= n ; i ++) { sum[bl[i]] += a[i] ; }
        for(register int i = 1 ; i <= n ; i ++) {
            int opt = read() ;
            if(opt == 0) {
                int l , r , c ;
                l = read() ; r = read() ; c = read() ;
                change(l , r , c) ;
            }
            if(opt == 1) {
                int l , r , c ;
                l = read() ; r = read() ; c = read() ;
                printf("%lld
    " , query(l , r , c)) ;
            }
        }
        return 0 ;
    }
    

    ( ext{分块6})
    分块6是可以区间插入一个数字 然后查询某个位置
    (其实用不到分块 (vector) 可以直接做)
    只是因为这一题没有区间查询 所以可以 (vector) 水过去
    如果想好好学分块就使用分块的做法
    当某个块大于 20 个块的时候 就重构块
    不然查询的时候比较费劲 而且暴力复杂度比较高

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC diagnostic error "-std=c++11"
    #include<bits/stdc++.h>
    
    using namespace std ;
    #define int long long
    inline int read() { register int x = 0 ; register int f = 1 ; register char c ;
    #define gc c = getchar()
    	while(isspace(gc)) ;
    	c == '-' ? gc , f = -1 : 0 ;
    	while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ;
    	return x * f ;
    }
    int n ;
    int unt ;
    const int N = 200000 + 5 ;
    int a[N] ;
    std::vector< int > v[N] ;
    int st[N] ;
    int top = 0 ; int m = 0 ;
    pair < int  , int > query(int b) {
        int x = 1 ;
        for ( ; b > v[x].size() ; ) b -= v[x ++].size() ;
        return make_pair(x , b - 1) ;
    }
    inline void Rebuild() {
        top = 0 ;
        for(register int i = 1 ; i <= m ; i ++) {
            for(auto j : v[i]) st[++ top] = j ;
            v[i].clear() ;
        }
        int blo = sqrt(top) ;
        for(register int i = 1 ; i <= top ; i ++) {
            v[(i - 1) / blo + 1].push_back(st[i]) ;
        }
        m = (top - 1) / blo + 1 ;
    }
    inline void Insert(int x , int y) {
        pair < int , int > p = query(x) ;
        v[p.first].insert(v[p.first].begin() + p.second , y) ;
        if(v[p.first].size() > 20 * unt) Rebuild() ;
    }
    signed main() {
        n = read() ; unt = sqrt(n) ;
        for(register int i = 1 ; i <= n ; i ++) a[i] = read() ;
        for(register int i = 1 ; i <= n ; i ++) v[(i - 1) / unt + 1].push_back(a[i]) ;
        m = (n - 1) / unt + 1 ;
        for(register int i = 1 ; i <= n ; i ++) {
            int opt = read() ;
            if(opt == 0) {
                int l = read() , r = read() , c = read() ;
                Insert(l , r) ;
            }
            if(opt == 1) {
                int l = read() , r = read() , c = read() ;
                pair < int , int > p = query(r) ;
                printf("%d
    " , v[p.first][p.second]) ;
            }
        }
        return 0 ;
    }
    

    ( ext{分块7})
    分块7是一题 ( ext{区间乘法 区间加法 单点查询}) 的题目
    那么我们只需要维护一下区间的值 乘法的值 加法的值就可以
    如果乘法那么就需要把加法的值乘上一个值
    不过在每次修改的时候 需要( ext{重构这个不完整的块所在的块})
    (好拗口 反正就是在(左,右区间)所在的块 然后把乘的值归1 然后加法的值清0
    这样的话复杂度仍然还是 (sqrt n)
    根据这个思路的话 我写了一个区间查询的
    我告诉我旁边的人:知道什么叫做单点查询吗
    -- (query(b,b))

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC diagnostic error "-std=c++11"
    #include<bits/stdc++.h>
    
    using namespace std ;
    #define int long long
    
    #define rep(i , j , n) for(register int i=j;i<=n;i++)
    #define Rep(i , j , n) for(register int i=j;i>=n;i--)
    #define gc c = getchar()
    #define int long long
    inline int read() { register int x = 0 , f = 1 ; register char c ;
        while(isspace(gc)) ; c == '-' ? f = -1 , gc : 0 ;
        while((x *= 10) += (c ^ 48) , isdigit(gc)) ;
        return x * f ;
    }
    
    using namespace std ;
    int n ;
    const int N = 100000 + 5 ;
    const int Unt = 2000 ;
    const int p = 10000 + 7 ;
    int a[N] ;
    int unt ;
    int block[N] ;
    int sum[Unt] ;
    int mul[Unt] ;
    int size[Unt] ;
    int ans[Unt] ;
    inline void reset(int x) {
        for(register int i=(x - 1) * unt + 1 ; i <= min(n , x * unt) ; i ++) {
            a[i] = (a[i] * mul[x] + sum[x]) % p ;
        }
        sum[x] = 0 ; mul[x] = 1 ;
    }
    inline void change_mul(int l , int r , int x) {
        reset(block[l]) ;
        for(register int i=l;i<=min(r , block[l] * unt) ; i ++) {
            ans[block[l]] -= a[i] ;
            a[i] *= x ;
            a[i] %= p ;
            ans[block[l]] += a[i] ;
            ans[block[l]] %= p ;
        }
        if(block[l] != block[r]) {
            reset(block[r]) ;
            for(register int i=(block[r] - 1) * unt + 1 ; i <= r ; i ++) {
                ans[block[r]] -= a[i] ;
                a[i] *= x ;
                a[i] %= p ;
                ans[block[r]] += a[i] ;
                ans[block[r]] %= p ;
            }
            for(register int i=block[l] + 1 ; i <= block[r] - 1 ; i ++) {
                sum[i] *= x ;
                sum[i] %= p ;
                mul[i] *= x ;
                mul[i] %= p ;
                ans[i] *= x ;
                ans[i] %= p ;
            }
        }
    }
    inline void change_sum(int l , int r , int x) {
        reset(block[l]) ;
        for(register int i=l ; i <= min(r , block[l] * unt) ; i ++) {
            a[i] += x ;
            a[i] %= p ;
            ans[block[l]] += x ;
        }
        if(block[l] != block[r]) {
            reset(block[r]) ;
            for(register int i = (block[r] - 1) * unt + 1 ; i <= r ; i ++) {
                a[i] += x ;
                a[i] %= p ;
                ans[block[r]] += x ;
            }
            for(register int i=block[l] + 1 ; i <= block[r] - 1 ; i ++) {
                sum[i] += x ;
                sum[i] %= p ;
                ans[i] += x * size[i] ;
            }
        }
    }
    inline int Query(int l , int r) {
        int Ans = 0 ;
        for(register int i=l;i<=min(r , block[l] * unt) ; i ++)
            Ans += (a[i] * mul[block[l]] + sum[block[l]] ) % p ;
        if(block[l] != block[r]) {
            for(register int i = (block[r] - 1) * unt + 1 ; i <= r ; i ++)
                Ans += (a[i] * mul[block[r]] + sum[block[r]] ) % p ;
            for(register int i = block[l] + 1 ; i <= block[r] - 1 ; i ++)
                Ans += ans[i] ;
        }
        return Ans % p ;
    }
    signed main() {
        n = read() ;
        unt = sqrt(n) ;
        for(register int i=1;i<=n;i++) a[i] = read() ;
        for(register int i=1;i<=n;i++) {
            block[i] = (i - 1) / unt + 1 ;
            ans[block[i]] += a[i] ;
            size[block[i]] ++ ;
        }
        for(register int i=1;i<=block[n];i++) mul[i] = 1 ;
        for(register int i=1;i<=n;i++) {
            int opt = read() ;
            if(opt == 0) {
                int a = read() , b = read() , c = read() ;
                change_sum(a , b , c) ;
            }
            if(opt == 1) {
                int a = read() , b = read() , c = read() ;
                change_mul(a , b , c) ;
            }
            if(opt == 2) {
                int a = read() , b = read() , c = read() ;
                printf("%lld
    " , Query(b , b)) ;
             }
        }
        return 0 ;
    }
    

    ( ext{分块8})

    分块8的话是一个 查询区间有多少个(c) 并把整个区间改成 (c)

    同样我们考虑判重 把每个块用一个(flg)数组记录当前值
    然后对于左右区间的修改 把左右区间所在的块重构成之前的(flg)
    然后修改成c 再判断左右区间有多少个c
    那么对于( ext{完整的块}) 我们只需要 加上一个块的长度
    这样就可以做到判重的效果了
    对于 ( ext{完整的块}) 我们只需要修改当前块的(flg)值就可以了

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC diagnostic error "-std=c++11"
    #include<bits/stdc++.h>
    
    using namespace std ;
    #define int long long
    
    #define rep(i , j , n) for(register int i=j;i<=n;i++)
    #define Rep(i , j , n) for(register int i=j;i>=n;i--)
    #define gc c = getchar()
    #define int long long
    inline int read() { register int x = 0 , f = 1 ; register char c ;
        while(isspace(gc)) ; c == '-' ? f = -1 , gc : 0 ;
        while((x *= 10) += (c ^ 48) , isdigit(gc)) ;
        return x * f ;
    }
    
    const int N = 100000 + 5 ;
    int n ;
    int a[N] ;
    int unt ;
    int flg[N] ;
    int bl[N] ;
    inline void reset(int x) {
        if(flg[x] == -1) return ;
        for(register int i = (x - 1) * unt + 1 ; i <= x * unt ; i ++) a[i] = flg[x] ;
        flg[x] = -1 ;
    }
    inline int solve(int l , int r , int c) { int ans = 0 ;
        reset(bl[l]) ;
        for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) {
            if(a[i] != c) a[i] = c ;
            else ans ++ ;
        }
        if(bl[l] != bl[r]) {
            reset(bl[r]) ;
            for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) {
                if(a[i] != c) a[i] = c ;
                else ans ++ ;
            }
        }
        for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) {
            if(flg[i] != -1) {
                if(flg[i] != c) flg[i] = c ;
                else ans += unt ;
            }
            else {
                for(register int j = (i - 1) * unt + 1 ; j <= i * unt ; j ++)
                    if(a[j] != c) a[j] = c ;
                    else ans ++ ;
                flg[i] = c ;
            }
        }
        return ans ;
    }
    signed main() {
        n = read() ; unt = sqrt(n) ;
        for(register int i = 1 ; i <= n ; i ++) a[i] = read() ;
        for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
        for(register int i = 1 ; i <= bl[n] ; i ++) flg[i] = -1 ;
        for(register int i = 1 ; i <= n ; i ++) {
            int l = read() , r = read() , c = read() ;
            printf("%lld
    " , solve(l , r , c)) ;
        }
        return 0 ;
    }
    

    ( ext{分块9})
    前置知识:离线莫队
    分块9的话我没想到怎么在线做
    就是按每次查询的 (l) 排序 然后瞎搞
    (大概是一个莫队的思想

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #pragma GCC diagnostic error "-std=c++11"
    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, j, n) for (register int i = j; i <= n; i++)
    #define Inc(i, j, n) for (register int i = j; i <= n; i++)
    #define Rep(i, j, n) for (register int i = j; i >= n; i--)
    
    inline int read() {
    	register int x = 0, f = 1;
    	register char c;
    #define gc c = getchar()
    	while (isspace(gc))
    		;
    	c == '-' ? f = -1, gc : 0;
    	while ((x *= 10) += (c ^ 48), isdigit(gc))
    		;
    	return x * f;
    }
    const int N = 1e5;
    int n, siz, k[N + 10], bl[N + 10];
    struct Que {
    	int L, r, idx;
    } q[N + 10];
    bool cmp(const Que &A, const Que &B) {
    	return bl[A.L] ^ bl[B.L] ? A.L < B.L : A.r < B.r;
    }
    inline void init() {
    	n = read() ;
    	Inc(i, 1, n) k[i] = read() ;
    	Inc(i, 1, n) {
    		int x, y;
    		x = read() , y = read() ;
    		q[i] = (Que) {
    			x, y, i
    		};
    	}
    	siz = sqrt(n);
    	Inc(i, 1, n) bl[i] = (i - 1) / siz + 1;
    	sort(q + 1, q + 1 + n, cmp);
    }
    int rp[N + 10];
    inline void disc() {
    	int tmp[N + 10];
    	Inc(i, 1, n) tmp[i] = k[i];
    	sort(tmp + 1, tmp + 1 + n);
    	int len = unique(tmp + 1, tmp + 1 + n) - tmp - 1;
    	Inc(i, 1, n) rp[i] = lower_bound(tmp + 1, tmp + 1 + len, k[i]) - tmp;
    }
    int cur_ans, cur_num, Ans[N + 10], num[N + 10];
    inline void addr(int x) {
    	++num[rp[x]];
    	if (num[rp[x]] >= cur_num) {
    		if (num[rp[x]] == cur_num && k[x] < cur_ans)
    			cur_ans = k[x];
    		else if (num[rp[x]] > cur_num)
    			cur_ans = k[x], cur_num = num[rp[x]];
    	}
    }
    inline void addl(int x, int &ans, int &Num) {
    	++num[rp[x]];
    	if (num[rp[x]] >= Num) {
    		if (num[rp[x]] == Num && k[x] < ans)
    			ans = k[x];
    		else if (num[rp[x]] > Num)
    			ans = k[x], Num = num[rp[x]];
    	}
    }
    inline void remove(int x) {
    	--num[rp[x]];
    }
    inline void solv() {
    	int L, r, lim;
    	Inc(i, 1, n) {
    		if (bl[q[i].L] ^ bl[q[i - 1].L]) {
    			memset(num, 0, sizeof(num));
    			L = lim = bl[q[i].L] * siz + 1;
    			r = bl[q[i].L] * siz;
    			cur_ans = cur_num = 0;
    		}
    		if (bl[q[i].L] == bl[q[i].r]) {
    			int ans, nownum = 0;
    			Inc(j, q[i].L, q[i].r)++ num[rp[j]];
    			Inc(j, q[i].L, q[i].r) if (num[rp[j]] >= nownum) {
    				if (num[rp[j]] == nownum && k[j] < ans)
    					ans = k[j];
    				else if (num[rp[j]] > nownum)
    					ans = k[j], nownum = num[rp[j]];
    			}
    			Inc(j, q[i].L, q[i].r)-- num[rp[j]];
    			Ans[q[i].idx] = ans;
    			continue;
    		}
    		while (r < q[i].r) addr(++r);
    		int now = cur_ans, nownum = cur_num;
    		while (L > q[i].L) addl(--L, now, nownum);
    		Ans[q[i].idx] = now;
    		while (L < lim) remove(L++);
    	}
    	Inc(i, 1, n) printf("%d
    " , Ans[i]) ;
    }
    signed main() {
    	init();
    	disc();
    	solv();
    	return 0;
    }
    

    ( ext{例题})

    https://loj.ac/problem/10117
    

    ( ext{LOJ10117})

    这题就是一个裸的树状数组 但是可以用分块做 好像更方便呢

    // Isaunoya
    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    #define rep(i , j , n) for(register int i = j ; i <= n ; i ++)
    #define Rep(i , j , n) for(register int i = j ; i >= n ; i --)
    #define to(u) for(register int i = head[u] ; i ; i = edge[i].nxt)
    
    inline int read() { register int x = 0 , f = 1 ; register char c ;
    #define gc c = getchar()
    	while(isspace(gc)) ; c == '-' ? f = -1 , gc : 0 ;
    	while(x = (x << 1) + (x << 3) + (c & 15) , isdigit(gc)) ;
    	return x * f ;
    }
    
    using namespace std ;
    int n ;
    const int N = 1e5 + 10 ;
    int a[N] ;
    
    int atag[N] ;
    int bl[N] ;
    int unt ;
    inline void change(int l , int r) {
    	for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++) a[i] ^= 1 ;
    	if(bl[l] != bl[r]) 
    		for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) a[i] ^= 1 ;
    	for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) atag[i] ^= 1 ;
    	return ;
    }
    signed main() {
    //	freopen(".in" , "r" , stdin) ; freopen(".out" , "w" , stdout) ;
    	n = read() ; unt = sqrt(n) ;
    	for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
    	for(register int q = read() ; q -- ; ) {
    		int opt = read() ;
    		if(opt & 1) {
    			int l = read() , r = read() ;
    			change(l , r) ;
    		}
    		else {
    			int x = read() ;
    			printf("%lld
    " , a[x] ^ atag[bl[x]]) ;
    		}
    	}
    	return 0 ;
    }
    
    
    https://www.luogu.org/problem/P3372
    https://www.luogu.org/problem/P3373
    https://www.luogu.org/problem/P2572
    https://www.luogu.org/problem/P3396
    https://www.luogu.org/problem/P3203
    https://www.luogu.org/problem/P4879
    https://www.luogu.org/problem/P3870
    https://www.luogu.org/problem/P2574
    https://www.luogu.org/problem/P2801
    https://www.luogu.org/problem/P4145
    
    
  • 相关阅读:
    TH-Union教学机 指令总结
    Manjaro 显卡驱动安装
    grub学习内容
    manjaro 折腾
    链栈的实现
    汇编综合实验
    二叉树
    Oracle表空间基本操作
    Windows7/10实现ICMP(ping命令)
    WireShark——IP协议包分析(Ping分析IP协议包)
  • 原文地址:https://www.cnblogs.com/Isaunoya/p/11617961.html
Copyright © 2011-2022 走看看