ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷 P2357 守墓人

2022-02-08 21:35:07  阅读:180  来源: 互联网

标签:cnt 洛谷 int 守墓 sum P2357 long zhi include


题干

由题意得,需要区间修改,区间查询

所以这个题可以用树状数组,线段树和分块来做

咱们今天讲分块的做法:

先理解分块,就会发现本题相当于板子题,整活

#include<iostream>
#include<cstdio>
#include<cmath>
#define NUM 200010
using namespace std;
struct dian{
    int k;
    long long zhi;
};
dian a[NUM];//存下每个点当前的数值zhi以及所在块的编号 
struct kuai{
    long long sum,tag;
    int l,r;
};
kuai b[1000];//存下每个块的范围(l-r),懒惰标记与块内总和 
int n,m;//点数量与询问数量 
int main(){
    cin >> n >> m;
    int w = (int)sqrt(n),k = n/w;//w是块的数量,k是每个满块的零点数量 
    if( w*w < n ) w++;//凑满 
    if( n == 3 ){w = 2;k = 2;}//特殊处理 
    if( n == 2 ){w = 2;k = 1;}
//    cout << "块的数量为:" << w << "  每个块里有数量为:" << k << endl; 
    int cnt = 0;
    for( int i = 1;i <= n;i++ )
        cin >> a[i].zhi;
    for( int i = 1;i <= w;i++ ){
        for( int j = 1;j <= k;j++ ){
            a[++cnt].k = i;
            if( j == 1 ) b[i].l = cnt;//如果是新块的开头,说明l为该点 
            b[i].r = cnt;
            b[i].sum += a[cnt].zhi; 
            if( cnt >= n ) break;//所有块都已经就位 
        }
    }
    for( int i = 1;i <= w;i++ )
//        printf( "i = %d,sum = %d,l = %d,r = %d\n",i,b[i].sum,b[i].l,b[i].r );
    int l,r,p;
    for( int fi = 1;fi <= m;fi++ ){
        cin >> cnt;
        if( cnt == 1 ){
            cin >> l >> r >> p;
            bool ok = 0;
            for( int i = 1;i <= w;i++ ){ //枚举每个块 
                if( ok ) break;
                if( l <= b[i].l && b[i].r <= r ) b[i].tag += p;//这个块完全在范围中 
                else{
                    if( l <= b[i].r && b[i].l < l ) //是左边的碎块 
                        for( int j = l;j <= min(r,b[i].r);j++ ){
                            a[j].zhi += p;
                            b[a[j].k].sum += p;
                        }
                    if( b[i].l <= r && b[i].r > r ){ //右边的碎块 
                        ok = 1;//已经到头了 
                        for( int j = max(l,b[i].l);j <= r;j++ ){
                            a[j].zhi += p;
                            b[a[j].k].sum += p;
                        }
                    }
                }
            }
//            cout << "进行了区间修改:\n";
            for( int i = 1;i <= n;i++ )
                cout << a[i].zhi + b[a[i].k].tag << " ";
            cout << endl;
        }
        if( cnt == 2 ){
            cin >> p;
            a[1].zhi += p;
            b[1].sum += p;
//            cout << "当前主坟为:" << a[1].zhi+b[1].tag << endl;
            continue;
        }
        if( cnt == 3 ){
            cin >> p;
            a[1].zhi -= p;
            b[1].sum -= p;
//            cout << "当前主坟为:" << a[1].zhi+b[1].tag << endl;
            continue;
        }
        if( cnt == 4 ){
//            cout << "各个区间取和:" << endl;
            long long ans = 0;bool ok = 0;
            cin >> l >> r;
            for( int i = 1;i <= w;i++ ){
                if( ok ) break;
                if( l <= b[i].l && b[i].r <= r ){
                    ans += b[i].sum;
                    ans += (b[i].r-b[i].l+1) * b[i].tag;
                }
                else{
                    if( l <= b[i].r && b[i].l < l )
                        for( int j = l;j <= min(r,b[i].r);j++ ){
                            ans += a[j].zhi + b[a[j].k].tag; //当前每个数的数值加所在块的懒惰标记 
                        }
                    if( b[i].l <= r && b[i].r > r ){
                        ok = 1;
                        for( int j = max(l,b[i].l);j <= r;j++ )
                            ans += a[j].zhi + b[a[j].k].tag;
                    }
                }
//                cout << ans << " ";
            }
//            cout << endl << "区间和为:";
            cout << ans << endl;
            continue;
        }
        if( cnt == 5 ){
//            cout << "主坟墓为:";
            cout << a[1].zhi+b[1].tag << endl;
            continue;
        }
    }
    
    
    return 0;
}
我的理解

然而我的理解WA了,没明白错在哪

网上正解

#include<iostream>
#include<cstdio>
#include<cmath>
#define NUM 200010
using namespace std;
struct dian{
    int k;
    long long zhi;
};
dian a[NUM];
struct kuai{
    long long sum,tag;
    int l,r;
};
kuai b[1000];
int n,m;
int main(){
    cin >> n >> m;
    int w = (int)sqrt(n);//就是每个块的大小 
    cout << w << endl;
    for( int i = 1;i <= n;i++ ){
        cin >> a[i].zhi;
        a[i].k = (i-1) / w + 1;//每个点在哪个块,存下 
        b[a[i].k].sum += a[i].zhi;
    }
    cout << endl;
    for( int i = 1;i <= n;i++ )
        cout << a[i].k << " ";
    cout << endl;
    int l,r,p,cnt;
    for( int fi = 1;fi <= m;fi++ ){ //开始操作 
        cin >> cnt;
        if( cnt == 1 ){
            cin >> l >> r >> p;
            //add(l,r,q)__________________________ 
          / for( int i = l;i <= min(r,a[l].k*w);i++ ){ //处理左边碎块
         /  //从最左端点开始,一直到a[l].k*w,就是第k个块的结尾的点编号 
        /       a[i].zhi += p;
       /        b[a[i].k].sum += p;
      /     }
     /      if( a[l].k != a[r].k ){ //如果不止在一个块里 
    /         for( int i = (a[r].k-1)*w+1;i <= r;i++ ) //处理右边碎块 
    \         //从前结尾前一个块的最后+1(即结尾所在块的第一个)开始,一直到 
     \          a[i].zhi += p,b[a[i].k].sum += p;//碎块本身加,所在大块加 
      \     }
       \    //处理范围所包含的整块
        \    //开始于起点所在的碎块的后一个块(即第一个整块),结束于终点的前一个块(最后一个整块) 
         \  for( int i = a[l].k+1;i <= a[r].k-1;i++ ) b[i].tag += p;
//        \ ____________________________________
        }
        if( cnt == 2 ){
            cin >> p;
            a[1].zhi += p;
            b[1].sum += p;
            continue;
        }
        if( cnt == 3 ){
            cin >> p;
            a[1].zhi -= p;
            b[1].sum -= p;
            continue;
        }
        if( cnt == 4 ){
            long long ans = 0;bool ok = 0;
            cin >> l >> r;
            //query(l,r) ,原理类似于上面的add函数 
            for( int i = l;i <= min(r,a[l].k * w);i++ )
                ans += a[i].zhi + b[a[i].k].tag;
            if( a[l].k != a[r].k ){
                for( int i = (a[r].k-1)*w+1;i <= r;i++ )
                    ans += a[i].zhi + b[a[i].k].tag;
            }
            for( int i = a[l].k+1;i < a[r].k;i++ )
                ans += b[i].sum + b[i].tag * w;
            cout << ans << endl;
            continue;
        }
        if( cnt == 5 ){
            cout << a[1].zhi+b[1].tag << endl;
            continue;
        }
    }
    return 0;
}
网上正解

要注意每次使用真实的$a[i].zhi$时一点要加上$b[a[i].k].tag$,即每次取点值的时候要加上所在块的懒惰数值

算是对分块的基本了解了

标签:cnt,洛谷,int,守墓,sum,P2357,long,zhi,include
来源: https://www.cnblogs.com/xiao-en/p/15872908.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有