关于分块问题的一些感想
来源:互联网 发布:会计中级网络课程 编辑:程序博客网 时间:2024/06/16 12:33
看了bestcoder的一道题目,真心发现分块思想是一个很神奇的东西,我们所熟知的线段树其实就是分块思想的一种体现,而分块并不一定要按照线段树一样的进行二分,也可以对整个集合划分成若干段,然后利用预处理的每段信息进行查询操作,常见的就是划分成sqrt(n)段,见到有大神称之为“根号n法”,感觉挺贴切的,另外在看bc的题解的时候还学会了一种比较不错的hash方法,代码来自huanzhizun。
#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<cmath>#include<vector>#include<map>using namespace std;const int maxn=51005;const int mod=1000000007;struct pi{ int sum; int lson; int rson;}pp[maxn*17];int root[maxn],tot;void build(int cnt,int l,int r){ pp[cnt].sum=0; if(l==r) return; pp[cnt].lson=tot+1; tot++; build(tot,l,(l+r)/2); pp[cnt].rson=tot+1; tot++; build(tot,(l+r)/2+1,r);}void merg(int qq,int cnt,int n,int p,int k){ int le,ri,mid; le=1; ri=n; while(le<=ri){ mid=(le+ri)/2; pp[cnt]=pp[qq]; pp[cnt].sum+=k; if (le==ri) break; if(p<=mid){ pp[cnt].lson=tot+1; tot++; ri=mid; cnt=tot; qq=pp[qq].lson; } else{ pp[cnt].rson=tot+1; tot++; le=mid+1; cnt=tot; qq=pp[qq].rson; } }}int query(int cnt,int le,int ri,int l,int r){ int s=0; int mid; if(le>=l&&ri<=r){ return pp[cnt].sum; } mid=(le+ri)/2; if(l<=mid) s+=query(pp[cnt].lson,le,mid,l,r); if(r>mid) s+=query(pp[cnt].rson,mid+1,ri,l,r); return s;}int a[maxn],b[maxn],c[maxn];int l[maxn],vis[maxn],r[505],le[505];int g[315][315];vector<int>gg[maxn],g1;//map<int ,int>mm[505];int d[maxn][300];int main(){ int i,j,n,m,t; cin>>t; while(t--){ scanf("%d%d",&n,&m); memset(vis,0,sizeof(vis)); for(i=1;i<=n;i++){ scanf("%d",&a[i]); b[i]=a[i]; gg[i].clear(); } sort(b+1,b+1+n); memset(d,0,sizeof(d));//利用排序将a中的数字hash成在b中对应位置的编号 for(i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+1+n,a[i])-b; vis[a[i]]++; }//预处理计算各个数的k次方的结果 for(i=1;i<=n;i++){ long long q=1; gg[i].push_back(0); for(j=1;j<=vis[i];j++){ q=q*b[i]; q%=mod; gg[i].push_back(q); } }//f表当前块,le和r表示当前块的左右端点,l[i]表i所属的块,d[i][j]表i号元素在前j个块中出现的次数 int p=sqrt((double)n),f=0; for(i=1;i<=n;i++){ f++; int q=min(i+p-1,n); r[f]=q; le[f]=i; for(j=1;j<=n;j++){ d[j][f]=d[j][f-1]; } for(j=i;j<=q;j++){ l[j]=f; d[a[j]][f]++; } i=q; }//计算g数组 memset(g,0,sizeof(g)); for(i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); for(j=i;j<=n;j++){ vis[a[j]]++; if(j==i||l[j]!=l[j-1]){ g[l[i]][l[j]]=(g[l[i]][l[j]-1]+gg[a[j]][vis[a[j]]])%mod-gg[a[j]][vis[a[j]]-1]; g[l[i]][l[j]]%=mod; } else{ g[l[i]][l[j]]+=(gg[a[j]][vis[a[j]]]-gg[a[j]][vis[a[j]]-1])%mod; g[l[i]][l[j]]%=mod; } } i+=p-1; }//处理询问 memset(vis,0,sizeof(vis)); int la=0; for(i=0;i<m;i++){ int x,y,ll,rr; scanf("%d%d",&x,&y); ll=x; rr=y; x=min((ll^la)%n+1,(rr^la)%n+1); y=max((ll^la)%n+1,(rr^la)%n+1); g1.clear(); if(l[y]-l[x]<=1){ for(j=x;j<=y;j++){ if(vis[a[j]]==0) g1.push_back(a[j]); vis[a[j]]++; } int s=0; int p=g1.size(); for(j=0;j<p;j++){ s+=gg[g1[j]][vis[g1[j]]]; vis[g1[j]]=0; s%=mod; } s=(s+mod)%mod; printf("%d\n",s); la=s; } else{ int s=0; s=g[l[x]+1][l[y]-1]; for(j=x;j<=r[l[x]];j++){ if(!vis[a[j]]) g1.push_back(a[j]); vis[a[j]]++; } for(j=le[l[y]];j<=y;j++){ if(!vis[a[j]]) g1.push_back(a[j]); vis[a[j]]++; } int p=g1.size(); for(j=0;j<p;j++){ int v=g1[j]; int q=d[v][l[y]-1]-d[v][l[x]]; s+=(gg[v][q+vis[v]]-gg[v][q])%mod; s%=mod; vis[v]=0; } s=(s%mod+mod)%mod; printf("%d\n",s); la=s; } } }}
另外附上bc的官方题解:
0 0
- 关于分块问题的一些感想
- 关于博客的一些感想
- 关于研发的一些感想
- 一些关于工作的感想
- 关于2012的一些感想
- 关于android的一些感想
- 关于培训的一些感想
- 关于目标的一些感想
- 关于找工作的一些感想
- 关于测试的一些感想
- 关于工作的一些感想
- 关于面试的一些感想
- 关于编程的一些感想
- 关于创业的一些感想
- 关于RPC的一些感想
- 关于树分块算法的一些研究
- 关于医院管理软件的一些感想
- 关于java和.net的一些感想
- EALayout 第七节 控件属性设置
- 反向代理
- Lua实现的Base64编码
- Spring MVC学习-----------异常处理以及日志记录
- 异常处理(Python)
- 关于分块问题的一些感想
- getCacheDir()、getFilesDir()、getExternalFilesDir()、getExternalCacheDir()的作用
- Mac OS X上使用Wireshark抓包
- hdu3665Seaside 最短路水题
- 在rails的console下调用helper方法
- https://123.56.95.148/svn/Source/secretary
- 求约数 hdu2601 An easy problem
- sublime text 2or3 设置和注册码licenses
- 机器视觉代码集合