【题目描述】
定义一个整数集合S,S中的元素满足在十进制表示下只有5和7,例如5、7、57、75。
对于一个有n个元素的数列{a}进行以下两种操作:
操作一:add l r v,表示对于数列中加上v。
操作二:query l r,表示询问数列中有多少个满足。
【输入格式】
第一行输入两个整数n、m,分别表示数列长度和操作次数。
第二行输入n个整数,表示这个数列。
接下来的m行,每一行输入一个操作,操作格式如上。
【输出格式】
对于每一个询问操作输出一行一个整数。
【样例输入】
10 5
1 5 7 2 5 75 8 7 9 57
query 2 7
add 1 5 2
query 1 5
add 3 8 -1
query 1 10
【样例输出】
4
2
3
【数据范围】
n≤10^5,m≤10^5
对于所有数据保证任意时刻的ai∈[1,10000]
【提示】
注意常数因子对效率的影响。
【来源】
By tsy
题解:分块
令K表示在10000以内S集合的元素个数,经手数K=30。
把序列划分成长度为B的若干块。对于一个块,用cnt[i]表示i在每块中的个数,同时维护一个加法标记。
接下来考虑如何处理这两种操作:
Add l r:对于块内的元素,暴力修改,时间复杂度O(B)。对于连续的块,修改标记即可,时间复杂度O(n/B)。
Query l r v:对于块外的元素,标记下传暴力查询,时间复杂度O(B)。对于连续的块,假设已经打上了Δx的标记,就对于每一个L∈S,统计块中有多少个L-Δx,时间复杂度O(n*K/B)。
总时间复杂度:O(m*(B+n*K/B))。当B=sqrt(n*K)时,时间复杂度最小。
关于分块,这里写得很详细:http://blog.youkuaiyun.com/XianHaoMing/article/details/52201698
#include<iostream> #include<cstdio> #include<cmath> const int T=1750;//T=sqrt( n*k ) const int N=1e5+10; int n, m; void Getin( int &num ){ char c; int f=1; num=0; for( c=getchar(); c<'0' || c>'9'; c=getchar() ) if( c=='-' ) f=-1; for( ; c>='0'&&c<='9'; c=getchar() ) num=num*10+c-'0'; num*=f; } bool flg[10005]; int sp[35]={ 0, 5, 7, 55, 57, 75, 77, 555, 557, 575, 577, 755, 757, 775, 777, 5555, 5557, 5575, 5577, 5755, 5757, 5775, 5777, 7555, 7557, 7575, 7577, 7755, 7757, 7775, 7777 }; void Pre() { for( int i=1; i<=30; i++ ) flg[ sp[i] ]=1; } int num[N]; struct node{ int area, p; }pos[N]; struct block{ int len, add, num[T], cnt[10005]; }B[T]; void Build() { int l=(int)sqrt( 30*n+0.5 ), cnt=1; for( int i=1; i<=n; i++ ) { if( B[cnt].len==l ) cnt++; B[cnt].num[ ++B[cnt].len ]=num[i]; B[cnt].cnt[ num[i] ]++; pos[i].area=cnt;//记录数字在哪一块和在那一块的位置 pos[i].p=B[cnt].len; } } void Change( int P ) { if( !B[P].add ) return; for( int i=1; i<=B[P].len; i++ ) { B[P].cnt[ B[P].num[i] ]--; B[P].num[i]+=B[P].add; B[P].cnt[ B[P].num[i] ]++; } B[P].add=0; } void Change( int l, int r, int P, int add ) { if( l==1 && r==B[P].len ) { B[P].add+=add; return; } Change( P ); for( ; l<=r; l++ ) { B[P].cnt[ B[P].num[l] ]--; B[P].num[l]+=add; B[P].cnt[ B[P].num[l] ]++; } } void Add( int l, int r, int add ) { int L=pos[l].area, R=pos[r].area; if( L==R ) { Change( pos[l].p, pos[r].p, L, add ); return; }//同一块 Change( pos[l].p, B[L].len , L, add ); Change( 1, pos[r].p, R, add );//不同块, 暴力处理最左右 for( L++; L<R; L++ ) B[L].add+=add;//加上中间块 } int Sum( int P ) { int ans=0; for( int i=1; i<=30; i++ ) if( sp[i]-B[P].add>=0 && sp[i]-B[P].add<=10000 ) ans+=B[P].cnt[ sp[i]-B[P].add ]; return ans; } int Sum( int l, int r, int P ) { if( l==1 && r==B[P].len ) return Sum( P ); Change( P ); int ans=0; for( ; l<=r; l++ ) if( flg[ B[P].num[l] ] ) ans++; return ans; } int Query( int l, int r ) { int L=pos[l].area, R=pos[r].area; if( L==R ) return Sum( pos[l].p, pos[r].p, L );//同一块 int ans=Sum( pos[l].p, B[L].len, L )+Sum( 1, pos[r].p, R );//不同块, 暴力处理最左右 for( L++; L<R; L++ ) ans+=Sum( L );//加上中间块 return ans; } int l, r, v; char ord[10]; int main() { Pre(); Getin( n ); Getin( m ); for( int i=1; i<=n; i++ ) Getin( num[i] ); Build(); for( int i=1; i<=m; i++ ) { scanf( "%s", ord ); Getin( l ); Getin( r ); if( ord[0]=='a' ) Getin( v ), Add( l, r, v ); else printf( "%d\n", Query( l, r ) ); } return 0; }
另附标程,除了跑得特别快,其他的和我的代码没有区别 :)
#include <iostream> #include <cstdio> #include <cmath> #define MAXN 100005 using namespace std; char word, s[10]; bool ff; void GET(int &t) { t=0, ff=0; do{word=getchar();if(word=='-')ff=1;}while(word<'0'||word>'9'); do{t=t*10+word-'0';word=getchar();}while(word>='0'&&word<='9'); if(ff)t=-t; } int n, m, cnt, num[MAXN], pos[MAXN], pos2[MAXN]; const int a[35]={0,5,7,55,57,75,77,555,557,575,577,755,757,775,777,5555,5557, 5575,5577,5755,5757,5775,5777,7555,7557,7575,7577,7755,7757,7775,7777}; bool F[10005]; struct block { int num[1750], len, flag; int cnt[10005]; inline void add(int a) {++cnt[a], num[++len]=a;} inline void putdown() { if(flag) { for(int i=1;i<=len;++i) { --cnt[num[i]]; num[i]+=flag; ++cnt[num[i]]; } flag=0; } } inline void modify(int l,int r,int v) { if(l==1&&r==len){flag+=v;return;} putdown(); for(int i=l;i<=r;++i) { --cnt[num[i]]; num[i]+=v; ++cnt[num[i]]; } } inline int query() { int ans=0; for(int i=1;i<=30;++i) if( a[i]-flag<=10000 && a[i]-flag>=0 ) ans+=cnt[a[i]-flag]; return ans; } inline int query(int l,int r) { if(l==1&&r==len) return query(); putdown(); int ans=0; for(int i=l;i<=r;++i) if(F[num[i]])++ans; return ans; } }b[1750]; inline void build() { int l=(int)sqrt(n*26+0.5); cnt=1; for(int i=1;i<=n;++i) { if(b[cnt].len==l)++cnt; b[cnt].add(num[i]); pos[i]=cnt, pos2[i]=b[cnt].len; } } inline void add(int l,int r,int v) { int L=pos[l], R=pos[r]; if(L==R) { b[L].modify(pos2[l],pos2[r],v); return; } for(int i=L+1;i<R;++i)b[i].flag+=v; b[L].modify(pos2[l],b[L].len,v), b[R].modify(1,pos2[r],v); } inline int query(int l,int r) { int L=pos[l], R=pos[r]; if(L==R)return b[L].query(pos2[l],pos2[r]); int ans=b[L].query(pos2[l],b[L].len)+b[R].query(1,pos2[r]); for(int i=L+1;i<R;++i)ans+=b[i].query(); return ans; } int main() { for(int i=1;i<=30;++i) F[a[i]]=1; GET(n), GET(m); for(int i=1;i<=n;++i) GET(num[i]); build(); int l, r, v; while(m--) { scanf("%s",s), GET(l), GET(r); if(s[0]=='a') GET(v), add(l,r,v); else printf("%d\n",query(l,r)); } return 0; }
[NOIP模拟赛]统计
最新推荐文章于 2024-05-08 09:26:56 发布