hdu5057 分块处理,当数值大于数据范围时树状数组 真是巧 将大数据分为小数据来处理

这题说的给了100000个数有100000次操作 询问 L和R 区间内 在D位上为P的个数,用树状数组存 要开[10][10][100000]的int 开不了但是能开 这么大的unsign short 这样我们将这个树状数组一分为二 50000 个位前面 50000 为后面 我们知道unshort 范围在60000多显然这样是可以开的了

#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
typedef __int64 ll;
const int maxn=100005;
unsigned short C[2][10][10][50005];
int n;
int lowbit(int x){
  return x&(-x);
}
void add(int op, int id, int floor, int x,int v){
    int ma=op==0?min(50000,n):n-50000;
    while(x<=ma){
        C[op][id][floor][x]+=v;
        x+=lowbit(x);
    }
}
int sum( int op, int id, int floor, int x){
    int ans=0;
    while(x>0){
        ans+=C[op][id][floor][x];
        x-=lowbit(x);
    }
    return ans;
}
int A[maxn];
int solve(int L, int R, int D, int P){
        int s=0;
        if(R>50000){
            s=sum(1,P,D,R-50000)+sum(0,P,D,50000);
        }else s= sum(0,P,D,R);
        if(L>50000){
            s=s-sum(1,P,D,L-50000-1)-sum(0,P,D,50000);
        }else s-=sum(0,P,D,L-1);
        return s;
}
int main()
{
     int cas,m;
     scanf("%d",&cas);
     while(cas--){
         scanf("%d%d",&n,&m);
         memset(C,0,sizeof(C));
         for(int i=1; i<=n; ++i)
         {
             scanf("%d",&A[i]);
             int t =A[i];
             int loc=0;
             int ko=i>50000?i-50000:i;
             int op=i>50000?1:0;
             while(loc<10){
                add(op,t%10,loc,ko,1);
                loc++;
                t/=10;
             }
         }
         char st[2];
         int L,R,D,P;
         for(int i=0; i<m; ++i){
              scanf("%s",st);
              if(st[0]==‘Q‘){
                   scanf("%d%d%d%d",&L,&R,&D,&P);
                   int ans=solve(L,R,D-1,P);
                   printf("%d\n",ans);
              }else{
                  int x,y;
                  scanf("%d%d",&x,&y);
                  int t=A[x];
                  int loc=0;
                  int lc=x;
                  int op;
                  if(x>50000) lc-=50000,op=1;
                  else op=0;
                  while(loc<10){
                      add(op,t%10,loc,lc,-1);
                      loc++;
                      t/=10;
                  }
                  A[x]=y;
                  t = y;
                  loc=0;
                  while(loc<10){
                      add(op,t%10,loc,lc,1);
                      loc++;
                      t/=10;
                  }
            }
         }
     }
     return 0;
}

还有一种操作 就是离线,将每个操作记下来, 10000次操作 做10次 先做在第一位 开一个【100000】【10】的数组,然后先存每个数的第一位,然后按照操作顺序回答所有有关第一位的询问,然后同样的操作第二位 第三位... 意思做了10次这样的整套查询操作

#include <iostream>
#include <cstdio>
#include <string.h>
#include <vector>
using namespace std;
typedef __int64 ll;
const int maxn=100005;
int C[10][maxn];
int n,m;
int eps[10];
int chose(int V, int t){
   return V / eps[t] % 10;
}
int lowbit(int x){
  return x&(-x);
}
void add( int x, int id , int v){
     while(x<=n){
         C[id][x]+=v;
         x+=lowbit(x);
     }
}
int sum( int x, int id ){
    int ans=0;
    while(x>0){
       ans+=C[id][x];
       x-=lowbit(x);
    }
    return ans;
}
int A[maxn];
int B[maxn];
int ans[maxn];
int op[maxn],L[maxn],R[maxn],D[maxn],P[maxn];
bool use[11];
void inti(int lo){
    memset( C , 0 , sizeof( C ) );
    for(int i=1; i<=n; ++i){
        B[i]=A[ i ];
        int id=chose( B[ i ] , lo );
        add(i,id,1);
    }
}
void solve(int los){
     for(int i =0; i<m; ++i){
         if(op[i]==1){
              int x=L[i];
              add(x,chose(B[x],los),-1);
              B[x]=R[ i ];
              add( x , chose(B[x],los) , 1 );
         }else if( op[i]==2 && D[i]==los ){
              ans[i]=sum(R[i],P[i])-sum(L[i]-1,P[i]);
         }
     }
}
int main()
{
     eps[1]=1;
     for(int i=2; i<=10; ++i)
         eps[i]=eps[i-1]*10;
     int cas;
     scanf("%d",&cas);
     while(cas--){
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; ++ i){
            scanf("%d",&A[i]);
            A[i];
        }
        memset(use,false,sizeof(use));
        char ch[2];
        for(int i=0; i<m; ++i ){
            scanf("%s",ch);
             if(ch[0]==‘S‘){
                op[i]=1;
                scanf("%d%d",&L[i],&R[i]);
             }else{
                op[i]=2;
                scanf("%d%d%d%d",&L[i],&R[i],&D[i],&P[i]);
                use[ D[i] ] = true ;
             }
        }
        for(int i=1; i<=10; ++i){
             if(use[i]==false)continue;
             inti(i);
             solve(i);
        }
        for(int i=0; i<m; ++i){
             if(op[i]==2) printf("%d\n",ans[i]);
        }
     }
     return 0;
}

时间: 2024-11-03 21:21:08

hdu5057 分块处理,当数值大于数据范围时树状数组 真是巧 将大数据分为小数据来处理的相关文章

【BZOJ4167】永远的竹笋采摘 分块+树状数组

[BZOJ4167]永远的竹笋采摘 题解:我们考虑有多少点对(a,b)满足a与b的差值是[a,b]中最小的.以为是随机数据,这样的点对数目可能很少,实测是O(n)级别的,那么我们已知了有这么多可能对答案造成贡献的点对,如何将它们求出来呢? 考虑分块,因为所有数大小在[1,n]中,我们可以对于每个块,预处理出整个块到所有数的最小差值.然后从右往左枚举每一个点,再枚举右面所有的块,如果这个块到当前数的差值比之前的要小,那就暴力进入块中扫一遍.与此同时,我们需要知道是否已经存在这样的点对,被当前的点对

【分块】【树状数组】bzoj3744 Gty的妹子序列

离散化,分块. 预处理出:ans[i][j] 第i块到第j块的逆序对数. f[i][j] 第1~i块中大于j的数的个数. g[i][j] 第1~j块中小于j的数的个数. 每次询问时对于整块部分可以O(1)获得. 对于零散部分呢? >在一列数的后面添加一个数,逆序对数会增加 数列中比它大的数的个数. >在一列数的前面添加一个数,逆序对数会增加 数列中比它小的数的个数. 所以统计以上信息的时候,对于整块的部分,我们可以借由预处理的东西差分来O(1)地获得答案,零散的部分就是树状数组咯. 空间复杂度

[P3759][TJOI2017]不勤劳的图书管理员(分块+树状数组)

题目描述 加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员.他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生 这两本书页数的和的厌烦度.现在有n本被打乱顺序的书,在接下来m天中每天都会因为读者的阅览导致书籍顺序改变位置.因为小豆被要求在接下来的m天中至少 要整理一次图书.小豆想知道,如果他前i天不去整理,第i天他的厌烦度是多少,这样他好选择厌烦度最小的那天去整理. 输入输出格式 输入格式: 第一行会有两个数,n,m分别表示有n本书,m天 接下来n行,每行两

CF587F Duff is Mad(AC自动机+树状数组+分块)

考虑两一个暴力 1 因为询问\([a,b]\)可以拆成\([1,b]\)-\([1,a-1]\)所以把询问离线,然后就是求\([1,x]\)中被\(S_i\)包含的串的数量.考虑当\([1,x-1]->[1,x]\)时我们把\(S_x\)结束节点在fail树的子树加1.然后询问就是求\(S_i\)在AC自动机上跑时经过所有点的点权用树状数组维护.设\(\sum{len[S_i]}=L\)这样的复杂度就是\(O(mLlogL)\)无法通过此题. 2 依然离线.这次我们把\(S_i\)放在fail树

[ACM-ICPC 2018 沈阳网络赛] Ka Chang (dfs序+树状数组+分块)

Given a rooted tree ( the root is node 11 ) of NN nodes. Initially, each node has zero point. Then, you need to handle QQ operations. There're two types: 1\ L\ X1 L X: Increase points by XX of all nodes whose depth equals LL ( the depth of the root i

[数据结构学习]分块与树状数组

分块与树状数组均在区间问题上有重要的应用 emm分块效率上不如树状数组,但是思路比较好想 先说分块: 将n个数的序列分为sqrt(n)块,预处理每块数据的信息以加快后续对区间信息的查询 先上一段代码: const int maxn = 5e5 + 50; int sum[maxn],a[maxn],l[maxn],r[maxn],belong[maxn]; int block,num; void build(){ block = sqrt(n); num = n/block; if(n%bloc

【bzoj3744】Gty的妹子序列 分块+树状数组+主席树

题目描述 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间 [l,r]中妹子们美丽度的逆序对数吗?" 蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线." 请你帮助一下Autumn吧.

【树状数组】【权值分块】bzoj2352 Stars

经典问题:二维偏序.给定平面中的n个点,求每个点左下方的点的个数. 因为 所有点已经以y为第一关键字,x为第二关键字排好序,所以我们按读入顺序处理,仅仅需要计算x坐标小于<=某个点的点有多少个就行. 这就是所说的:n维偏序,一维排序,二维树状数组,三维 分治 Or 树状数组套平衡树…… <法一>树状数组. 1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using name

BZOJ 2141 排队 分块+树状数组

题目大意:给定一个序列,m次交换两个数,求初始逆序对数及每次交换后的逆序对数 首先离散化,分块,对于每块建立一个树状数组,保存这个块中的所有元素 然后对于每个询问(x,y) (x<y) 两侧的数是没有影响的,区间(x,y)的数a[i]讨论如下: a[i]<a[x] --ans a[i]>a[x] ++ans a[i]<a[y] ++ans a[i]>a[y] --ans 然后对于块中的树状数组处理,块外的暴力 注意此题元素有重复 亲测可信 RANK5吓尿0.0 为何块套树要比