hdu-1166 敌兵布阵 分块

11.06 感谢h学长在下午教我分块的基础,莫队的思想
这里大概讲一下我目前了解的分块,并记录一下菜鸡如何成长为菜鸟


以此题作为一个样例
题意:我们有n各阵营,每个阵营中有ai个人,接下来有多种操作

  1. add i,j 代表了第i个阵营增加了j个人
  2. sub i,j 代表了第i个阵营减少了j个人
  3. Query i,j询问 [i,j]区间内一共有多少人
    用s数组存n个数的时候下标从1开始,以免后面计算时的特判
  • 首先我们将n个数字分成sqtr(n)块那么每一块有blo = (int) sqrt( n )个数字,那么通过一个O(n)的预处理每一个数字的属于第(i-1)/blo+1块

      for(int i=1;i<=n;i++) scanf("%d",&s[i]),pos[i]=(i-1)/blo+1;
    
  • 我们访问每一块中的每一个元素,用一个 sum数组 记录每一块的总和,这样可以减少我们以后暴力的时间

      for(int i=1;i<=pos[n];i++){
          sum[i]=0;
          for(int j=(i-1)*blo+1;j<=min(n,i*blo);j++) sum[i]+=s[j];
      }
    
  • 接下来就是我们的修改或询问操作了

  • 修改
    • add 首先让第 i 个位置的数加上 j 大小并且这个数属于的块的总和也应该加上 j 的大小
    • sub 调用add函数即可,只需把加 j 改为加 -j
  • 询问区间 [i,j]
    • 我们预处理将每一块的总和放进了sum数组,但是 i 和 j 区间内不一定都为完整的一块(完整块)所以我们先将答案 ans+=sum[i](完整块的总和)在暴力去加上其余的s[i]

    • 那么完整块有哪些,i 的下一块到 j 的上一块不恰好是的完整块嘛,即[ pos[ i ] + 1, pos[ j ] - 1 ]

       *  for(int i=pos[l]+1;i<=pos[r]-1;i++) ans+=sum[i];
      
    • 加下来就是 特判(pos[ i ]是否等于pos[ j ])加上 暴力 加上每一个在pos[ i ]块,pos[ j ]块中的每一个元素了

      *  if(pos[l]!=pos[r]){
             for(int i=l;i<=pos[l]*blo;i++) ans+=s[i];
      	   for(int i=(pos[r]-1)*blo+1;i<=r;i++) ans+=s[i];
          }
          else for(int i=l;i<=r;i++) ans+=s[i];
      
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn=5e4+7;
int n;
int pos[maxn],blo,sum[300];
int s[maxn];

char ch[10];

void add(int x,int val){
    sum[pos[x]]+=val;
    s[x]+=val;
}

int query(int l,int r){
    int ans=0;
    for(int i=pos[l]+1;i<=pos[r]-1;i++) ans+=sum[i];
    if(pos[l]!=pos[r]){
        for(int i=l;i<=pos[l]*blo;i++) ans+=s[i];
        for(int i=(pos[r]-1)*blo+1;i<=r;i++) ans+=s[i];
    }
    else for(int i=l;i<=r;i++) ans+=s[i];
    return ans;
}

int l,r,val;

int main (){
    int t;scanf("%d",&t);
    for(int cas=1;cas<=t;cas++){
        scanf("%d",&n);blo=(int)sqrt(n*1.0);
        for(int i=1;i<=n;i++) scanf("%d",&s[i]),pos[i]=(i-1)/blo+1;
        for(int i=1;i<=pos[n];i++){
            sum[i]=0;
            for(int j=(i-1)*blo+1;j<=min(n,i*blo);j++) sum[i]+=s[j];
        }
        printf("Case %d:\n",cas);
        while(scanf("%s",ch)){
            if(ch[0]=='E') break;
            if(ch[0]=='A') {
                scanf("%d %d",&l,&r);
                add(l,r);
            }
            else if(ch[0]=='S') {
                scanf("%d %d",&l,&r);
                add(l,-r);
            }
            else {
                scanf("%d %d",&l,&r);
                printf("%d\n",query(l,r));
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值