zoukankan      html  css  js  c++  java
  • 分块入门(根据hzwer的博客。。)(右端点是r不是n。。)

    1.区间更新单点查询

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define maxn 100005
    int n, len, pos[maxn], a[maxn];
    struct Block {
        int add;
    } b[1000];
    void update(int l, int r, int c) {
        int p = pos[l], q = pos[r];
        if (p == q)
            for (int i = l; i <= r; i++) a[i] += c;
        else {
            for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
            for (int i = l; i <= p * len; i++) a[i] += c;
            for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c;
        }
    }
    int main() {
        cin >> n;
        len = sqrt(n);
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
        int opt, l, r, c;
        for (int i = 1; i <= n; i++) {
            scanf("%d%d%d%d", &opt, &l, &r, &c);
            if (opt == 0)
                update(l, r, c);
            else
                cout << a[r] + b[pos[r]].add << '
    ';
        }
    }

    2.区间求小于c的数的个数:先预处理,即把每块的元素sort一下,整块修改用add标记,查询用lower_bound,两端修改后用sort维护,查询暴力统计即可

    注意要把add当做块的属性,而不要下放到块中的元素里去

    /*
    预处理块时需要将块内元素进行排序,然后可以每次查询都是用二分来做
    区间更新时要将两端元素进行单独修改+排序
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define maxn 50005
    struct Block {
        int add;
        vector<int> v;
    } b[5005];
    int n, pos[maxn], len;
    int a[maxn];
    
    inline void calc(int p) {
        b[p].v.clear();
        for (int i = (p - 1) * len + 1; i <= min(p * len, n); i++) b[p].v.push_back(a[i]);
        sort(b[p].v.begin(), b[p].v.end());
    }
    void update(int l, int r, int c) {
        int p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) a[i] += c;
            calc(p);
            return;
        }
        for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
        for (int i = l; i <= min(p * len, n); i++) a[i] += c;
        for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c;
        calc(p);
        calc(q);
    }
    int query(int l, int r, int c) {
        int res = 0;
        int p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++)
                if (a[i] + b[p].add < c)
                    res++;
            return res;
        }
        for (int i = p + 1; i <= q - 1; i++) {
            int x = c - b[i].add;
            int tmp = lower_bound(b[i].v.begin(), b[i].v.end(), x) - b[i].v.begin();
            res += tmp;
        }
        for (int i = l; i <= min(p * len, n); i++)
            if (a[i] + b[p].add < c)
                res++;
        for (int i = (q - 1) * len + 1; i <= r; i++)
            if (a[i] + b[q].add < c)
                res++;
        return res;
    }
    int main() {
        cin >> n;
        len = sqrt(n);
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 1; i <= n; i++) {
            pos[i] = (i - 1) / len + 1;
            b[pos[i]].v.push_back(a[i]);
        }
        for (int i = 1; i <= pos[n]; i++)  //预处理先排序
            sort(b[i].v.begin(), b[i].v.end());
        int m = n;
        while (m--) {
            ll opt, l, r, c;
            scanf("%d%d%d%d", &opt, &l, &r, &c);
            if (opt == 0)
                update(l, r, c);
            else
                cout << query(l, r, c * c) << endl;
        }
    }

    3.区间更新+找区间内小于c的最大的数(c的前驱)

    块设为sqrt(n)就会t,设为1000就没事。。玄学复杂度啊

    /*
    set每次修改都是logn,那么n次块内更新耗时n*sqrt(n)*logsqrt(n)
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 100005
    struct Block {
        int add;
        set<int> s;
    } b[1005];
    int n, pos[maxn], len, a[maxn];
    set<int>::iterator it;
    
    void update(int l, int r, int c) {
        int p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) {
                b[p].s.erase(a[i]);
                a[i] += c;
                b[p].s.insert(a[i]);
            }
            return;
        }
        for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
        for (int i = l; i <= min(p * len, n); i++) {
            b[p].s.erase(a[i]);
            a[i] += c;
            b[p].s.insert(a[i]);
        }
        for (int i = (q - 1) * len + 1; i <= r; i++) {
            b[q].s.erase(a[i]);
            a[i] += c;
            b[q].s.insert(a[i]);
        }
    }
    int query(int l, int r, int c) {
        int res = -1, p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) {
                int x = a[i] + b[p].add;
                if (x < c)
                    res = max(res, x);
            }
            return res;
        }
        for (int i = p + 1; i <= q - 1; i++) {  //大块里用set查
            int x = c - b[i].add;
            it = b[i].s.lower_bound(x);
            if (it == b[i].s.begin())
                continue;
            it--;
            res = max(res, (*it) + b[i].add);
        }
        for (int i = l; i <= min(p * len, n); i++)
            if (a[i] + b[p].add < c)
                res = max(res, a[i] + b[p].add);
        for (int i = (q - 1) * len + 1; i <= r; i++)
            if (a[i] + b[q].add < c)
                res = max(res, a[i] + b[q].add);
        return res;
    }
    
    int main() {
        cin >> n;
        len = 1500;
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++) {
            pos[i] = (i - 1) / len + 1;
            b[pos[i]].s.insert(a[i]);
        }
        for (int i = 1; i <= n; i++) {
            int opt, l, r, c;
            scanf("%d%d%d%d", &opt, &l, &r, &c);
            if (opt == 0)
                update(l, r, c);
            else
                cout << query(l, r, c) << endl;
        }
    }

    4.区间更新,区间求和

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define maxn 50005
    struct Block {
        ll add, sum;
    } b[500];
    int n, pos[maxn], len;
    ll a[maxn];
    
    void update(int l, int r, int c) {
        int p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) a[i] += c, b[p].sum += c;
            return;
        }
        for (int i = p + 1; i <= q - 1; i++) b[i].add += c;
        for (int i = l; i <= min(p * len, n); i++) a[i] += c, b[p].sum += c;
        for (int i = (q - 1) * len + 1; i <= r; i++) a[i] += c, b[q].sum += c;
    }
    ll query(int l, int r, int c) {
        ll res = 0;
        int p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) res += a[i] + b[p].add;
            return res % c;
        }
        for (int i = p + 1; i <= q - 1; i++) res += b[i].sum, res += len * b[i].add;
        for (int i = l; i <= min(p * len, n); i++) res += b[p].add, res += a[i];
        for (int i = (q - 1) * len + 1; i <= r; i++) res += b[q].add, res += a[i];
        return res % c;
    }
    
    int main() {
        cin >> n;
        len = sqrt(n);
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 1; i <= n; i++) {
            pos[i] = (i - 1) / len + 1;
            b[pos[i]].sum += a[i];
        }
        for (int i = 1; i <= n; i++) {
            int opt, l, r, c;
            scanf("%d%d%d%d", &opt, &l, &r, &c);
            if (opt == 0)
                update(l, r, c);
            else
                cout << query(l, r, c + 1) << endl;
        }
    }

     5.区间开根(类似于势能线段树)

    /*
    2*2=4,4*4=16,16*16=256,每个数最多被开4次,开了4次以上就是1
    所以两端暴力开,整块判断块中所有元素是不是1,是的话那么这个块就不用开了
    修改最多4n,查询就是n*根号n
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 50005
    struct Block {
        int flag;
        int sum;
    } b[500];
    int n, len, pos[maxn], a[maxn];
    
    void calc(int p) {
        if (b[p].flag)
            return;
        b[p].flag = 1;
        b[p].sum = 0;
        for (int i = (p - 1) * len + 1; i <= p * len; i++) {
            a[i] = (int)sqrt(a[i]);
            b[p].sum += a[i];
            if (a[i] > 1)
                b[p].flag = 0;
        }
    }
    void update(int l, int r) {
        int p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) {
                b[p].sum -= a[i];
                a[i] = sqrt(a[i]);
                b[p].sum += a[i];
            }
            return;
        }
        for (int i = p + 1; i <= q - 1; i++) calc(i);
        for (int i = l; i <= min(r, p * len); i++) {
            b[p].sum -= a[i];
            a[i] = sqrt(a[i]);
            b[p].sum += a[i];
        }
        for (int i = (q - 1) * len + 1; i <= r; i++) {
            b[q].sum -= a[i];
            a[i] = sqrt(a[i]);
            b[q].sum += a[i];
        }
    }
    int query(int l, int r) {
        int res = 0, p = pos[l], q = pos[r];
        if (p == q) {
            for (int i = l; i <= r; i++) res += a[i];
            return res;
        }
        for (int i = p + 1; i <= q - 1; i++) res += b[i].sum;
        for (int i = l; i <= min(p * len, r); i++) res += a[i];
        for (int i = (q - 1) * len + 1; i <= r; i++) res += a[i];
        return res;
    }
    
    int main() {
        cin >> n;
        len = sqrt(n);
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 1; i <= n; i++) {
            pos[i] = (i - 1) / len + 1;
            b[pos[i]].sum += a[i];
        }
        for (int i = 1; i <= n; i++) {
            int opt, l, r, c;
            cin >> opt >> l >> r >> c;
            if (opt == 0)
                update(l, r);
            else
                cout << query(l, r) << endl;
        }
    }

    6.块状数组,块状链表:用来解决在数列中插入一个数

    /*
    块内维护一个数组和size
    再用前缀和log根号n 的时间内找到新来的数应该加在哪个块中 
    然后每增加一个数都要消耗该数所在块的长度的时间
    如果数据随机,那么一次插入就是O(根号n)
    
    如果不随机。。可能n个数都加在同一个块内,那么会导致复杂度退化为n^2 
    那么当块过大时进行重构即可,这里将块大小上限设置为20*根号n 
    */
    #include<bits/stdc++.h>
    #include<vector>
    using namespace std;
    #define maxn 200005
    struct Block{
        vector<int>v;
    }b[20005];
    int nn,n,len,m,pos[maxn],a[maxn];//块的长度,块的个数 
    
    void rebuild(){
        nn=0;
        for(int i=1;i<=m;i++){
            for(int j=0;j<b[i].v.size();j++)
                a[++nn]=b[i].v[j];
            b[i].v.clear();
        }
        len=sqrt(nn);m=(nn-1)/len+1;
        for(int i=1;i<=nn;i++){
            pos[i]=(i-1)/len+1;
            b[pos[i]].v.push_back(a[i]);
        }
    }
    void update(int pos,int x){//在位置pos插入x 
        int p=1;
        while(b[p].v.size()<pos)
            pos-=b[p].v.size(),p++;
        b[p].v.insert(b[p].v.begin()+pos-1,x);
        if(b[p].v.size()>20*len)
            rebuild();
    }
    int query(int pos){
        int p=1;
        while(b[p].v.size()<pos)
            pos-=b[p].v.size(),p++;
        return b[p].v[pos-1];
    }
    int main(){
        cin>>n;len=sqrt(n);
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++){
            pos[i]=(i-1)/len+1;
            b[pos[i]].v.push_back(a[i]);
        }
        m=pos[n];
        for(int i=1;i<=n;i++){
            int opt,l,r,c;
            cin>>opt>>l>>r>>c;
            if(opt==0)update(l,r);
            else cout<<query(r)<<endl;
        }
    }

     7.标记下传顺序(标记优先级)

    /*
    把数字表示成a*mul+add的形式 
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 100005
    #define mod 10007
    #define ll long long 
    struct Block{
        int mul,add;
    }b[1005];
    int a[maxn],n,len,pos[maxn];
    
    void pushdown(int p){
        for(int i=(p-1)*len+1;i<=min(p*len,n);i++)
            a[i]=((ll)a[i]*b[p].mul+b[p].add)%mod;
        b[p].add=0;b[p].mul=1;
    }/*
    void add(int l,int r,int c){
        int p=pos[l],q=pos[r];
        if(p==q){
            pushdown(p);
            for(int i=l;i<=r;i++)
                a[i]=(a[i]+c)%mod;
            return;    
        }
        for(int i=p+1;i<=q-1;i++)
            b[i].add=(b[i].add+(ll)b[i].mul*c)%mod;
        pushdown(p);pushdown(q);
        for(int i=l;i<=min(p*len,r);i++)
            a[i]=(a[i]+c)%mod;
        for(int i=(q-1)*len+1;i<=r;i++)
            a[i]=(a[i]+c)%mod;
    }
    void mul(int l,int r,int c){
        int p=pos[l],q=pos[r];
        if(p==q){
            pushdown(p);
            for(int i=l;i<=r;i++)
                a[i]=((ll)a[i]*c)%mod;
            return;
        }
        for(int i=p+1;i<=q-1;i++){
            b[i].mul=((ll)b[i].mul*c)%mod;
            b[i].add=((ll)b[i].add*c)%mod;
        }
        pushdown(p);pushdown(q);
        for(int i=1;i<=min(p*len,r);i++)
            a[i]=((ll)a[i]*c)%mod;
        for(int i=(q-1)*len+1;i<=r;i++)
            a[i]=((ll)a[i]*c)%mod;
    }*/
    void update(int f,int l,int r,int c){
        int p=pos[l],q=pos[r];
        pushdown(p);
        for(int i=l;i<=min(r,p*len);i++){
            if(f==0)a[i]+=c;
            else a[i]*=c;
            a[i]%=mod;
        }
        if(p!=q){
            pushdown(q);
            for(int i=(q-1)*len+1;i<=r;i++){
                if(f==0)a[i]+=c;
                else a[i]*=c;
                a[i]%=mod;
            }    
        }
        for(int i=p+1;i<=q-1;i++){
            if(f==0)
                b[i].add=(b[i].add+c)%mod;
            else {
                b[i].mul=(b[i].mul*c)%mod;
                b[i].add=(b[i].add*c)%mod;
            }
        }
    }
    int query(int x){
        int p=pos[x];
        return ((ll)a[x]*b[p].mul+b[p].add)%mod;
    }
    int main(){
        cin>>n;len=sqrt(n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            pos[i]=(i-1)/len+1;
        for(int i=1;i<=pos[n];i++)
            b[i].mul=1;
        for(int i=1;i<=n;i++){
            int opt,l,r,c;
            scanf("%d%d%d%d",&opt,&l,&r,&c);
            if(opt==2)cout<<query(r)<<endl;
            else update(opt,l,r,c);
        }
    }

     8.区间覆盖

    /*
    区间众数
    覆盖标记
     
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 100005
    struct Block{
        int c; 
    }b[500];
    int a[maxn],n,pos[maxn],len; 
    void pushdown(int p){
        if(b[p].c!=-1){
            for(int i=(p-1)*len+1;i<=p*len;i++)
                a[i]=b[p].c;
            b[p].c=-1;
        }
    }
    int query(int l,int r,int c){
        int res=0,p=pos[l],q=pos[r];
        if(p==q){
            pushdown(p);
            for(int i=l;i<=r;i++)
                if(a[i]==c)res++;
                else a[i]=c;
            return res;
        } 
        pushdown(p);pushdown(q);
        for(int i=l;i<=min(p*len,r);i++)
            if(a[i]==c)res++;
            else a[i]=c;
            
        for(int i=(q-1)*len+1;i<=r;i++)
            if(a[i]==c)res++;
            else a[i]=c;
            
        for(int i=p+1;i<=q-1;i++)
            if(b[i].c!=-1){
                if(b[i].c==c)res+=len;
                else b[i].c=c;
            }
            else {
                for(int j=(i-1)*len+1;j<=i*len;j++)
                    if(a[j]==c)res++;
                    else a[j]=c;
                b[i].c=c;
            }
         return res;
    }
    
    int main(){
        cin>>n;len=sqrt(n);
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)
            pos[i]=(i-1)/len+1,b[pos[i]].c=-1;
        for(int i=1;i<=n;i++){
            int l,r,c;
            cin>>l>>r>>c;
            cout<<query(l,r,c)<<endl;
        } 
    }
  • 相关阅读:
    IE8下网页中的视频会遮挡住顶层DIV的解决办法
    Synchronized 偏向锁、轻量级锁、自旋锁、锁消除
    Lock的使用
    Synchronized与ReentrantLock区别总结(简单粗暴,一目了然)
    Java线程池 面试题(精简)
    Java 线程池的认识和使用
    bat等大公司常考java多线程面试题
    Java面试题必备知识之ThreadLocal
    阿里面试题
    Spring中Bean的生命周期
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10575330.html
Copyright © 2011-2022 走看看