Codeforces 121 E. Lucky Array
問題
各桁の数字が全て4または7である数をlucky numberという。
例えば(4,7,477など)
n項の数列に対してm個のクエリに答えよ。
制約条件
n,m≦10^5
加算の後で数列の各項は10^4を超えない
方針
平方分割。
√nくらいのバケットに区切っておいて、
区間に一様に加算される値を管理する。
また、区間ごとに、(一様に加算される値を除いたときの)値の分布を配列でもっておく。
更新およびカウントのクエリは、
バケットからはみでる部分と、バケットに完全に含まれる部分に分けて処理できる。
バケットからはみ出る部分はナイーブに更新して、
バケットに含まれる部分は一括して更新する。
すると、カウントがO((lucky numberの数)*√n)くらいで、更新がO(√n)できる。
ソースコード
const int SZ=400; int n,m,a[100000],sum[400]; int cnt[400][10000]; bool lucky[10000]; vi vv; void gen(int c){ if(c)vv.pb(c); if(c*10+4<10000)gen(c*10+4); if(c*10+7<10000)gen(c*10+7); } int query(int l,int r){ int res=0; while(l<r&&l%SZ!=0)res+=lucky[a[l]+sum[l/SZ]], l++; while(l<r&&r%SZ!=0)r--, res+=lucky[a[r]+sum[r/SZ]]; l/=SZ; r/=SZ; for(;l<r;l++)rep(i,vv.size())res+=(sum[l]<=vv[i]?cnt[l][vv[i]-sum[l]]:0); return res; } void add(int l,int r,int x){ while(l<r&&l%SZ!=0){ cnt[l/SZ][a[l]]--; a[l]+=x; cnt[l/SZ][a[l]]++; l++; } while(l<r&&r%SZ!=0){ r--; cnt[r/SZ][a[r]]--; a[r]+=x; cnt[r/SZ][a[r]]++; } l/=SZ; r/=SZ; for(;l<r;l++)sum[l]+=x; } void run(){ vv.clear(); gen(0); rep(i,vv.size())lucky[vv[i]]=1; cin>>n>>m; rep(i,n){ cin>>a[i]; cnt[i/SZ][a[i]]++; } rep(i,m){ int l,r,x; string op; cin>>op>>l>>r; if(op[0]=='c')cout<<query(l-1,r)<<endl; else{ cin>>x; add(l-1,r,x); } } }