定义 S 为十进制只由4和7组成的全体正整数的集合。
给定数列 {an} ,要求支持以下两个操作。
∙add(l,r,x) :将所有 ai(l≤i≤r) 加上 x
∙count(l,r) :求 |{i|l≤i≤r,ai∈S}|
1≤n,m≤100000,1≤ai,x≤10000
所有操作结束后,保证 1≤ai≤10000 。
分析题目,观察条件。所有操作后保证 a 数组每个数是小于等于 10000 的正整数,且初值也是小于等于 10000 的正整数,并且加的数大于 0 。这说明了什么?每次操作结束之后 ai 都小于等于 10000 .
我们根据组合数知识,算出 10000 以内 S 集合的数有 30(=24+23+22+2) 个,设这个总数为 k 。
分块大法好!分块大法好!分块大法好!(很重要说三遍)
我们考虑分块算法,设每块大小为 b ,我们在块内用数组 t 维护每个数出现次数。
执行修改操作时:对于块外的数,我们可以直接重构整个块,时间复杂度为 O(b) 。对于连续的块,我们可以将修改值加进块上标记里,时间复杂度 O(nb) 。
执行查询操作时:对于块外的数,我们可以直接重构整个块,然后暴力统计,时间复杂度 O(b) 。对于连续的块,设某个块标记为 +Δx ,那么我们可以统计 ∑i∈Sti−Δx ,时间复杂度为 O(nkb) 。
然后总的时间复杂度为 O(m(b+nkb)) ,当 b=nk−−√ 时,时间复杂度最优,为 O(mnk−−√) 。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int B=1800;
const int A=10000;
const int K=30;
const int N=100000;
bool iss[A+1];
int a[N+1];
int s[K+1];
int n,m,b,cnt;
struct block
{
int l,r,mark;
int bin[A+1];
inline void rebuild()
{
if (!mark)
return;
for (int i=l;i<=r;i++)
bin[a[i]]--;
for (int i=l;i<=r;i++)
{
a[i]+=mark;
bin[a[i]]++;
}
mark=0;
}
inline int query(int st,int en)
{
rebuild();
int ret=0;
for (int i=st;i<=en;i++)
ret+=iss[a[i]];
return ret;
}
inline void change(int st,int en,int edit)
{
rebuild();
int ret=0;
for (int i=st;i<=en;i++)
{
bin[a[i]]--;
a[i]+=edit;
bin[a[i]]++;
}
}
}bs[B+1];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9')
{
if (ch=='-')
f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline bool check(int x)
{
while (x)
{
if (x%10!=4&&x%10!=7)
return false;
x/=10;
}
return true;
}
inline void preparation()
{
for (int i=1;i<=A;i++)
if (check(i))
{
iss[i]=true;
s[++s[0]]=i;
}
}
int main()
{
preparation();
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
n=read(),m=read();
for (int i=1;i<=n;i++)
a[i]=read();
b=floor(sqrt(n*K))+1;
cnt=n/b;
for (int i=1;i<=cnt;i++)
{
bs[i].l=(i-1)*b+1;
bs[i].r=i*b;
for (int j=bs[i].l;j<=bs[i].r;j++)
bs[i].bin[a[j]]++;
}
if (n%b)
{
cnt++;
bs[cnt].l=(cnt-1)*b+1;
bs[cnt].r=n;
for (int j=bs[cnt].l;j<=bs[cnt].r;j++)
bs[cnt].bin[a[j]]++;
}
char oprt;
int l,r,x;
while (m--)
{
oprt=getchar();
while (oprt!='a'&&oprt!='c')
oprt=getchar();
l=read(),r=read();
if (oprt=='a')
{
x=read();
int lt=(l-1)/b+1,rt=(r-1)/b+1;
if (lt==rt)
{
bs[lt].change(l,r,x);
continue;
}
if (l!=bs[lt].l)
{
bs[lt].change(l,bs[lt].r,x);
lt++;
}
if (r!=bs[rt].r)
{
bs[rt].change(bs[rt].l,r,x);
rt--;
}
for (int i=lt;i<=rt;i++)
bs[i].mark+=x;
}
else
{
int lt=(l-1)/b+1,rt=(r-1)/b+1;
if (lt==rt)
{
printf("%d\n",bs[lt].query(l,r));
continue;
}
int ans=0;
if (l!=bs[lt].l)
{
ans+=bs[lt].query(l,bs[lt].r);
lt++;
}
if (r!=bs[rt].r)
{
ans+=bs[rt].query(bs[rt].l,r);
rt--;
}
for (int i=lt;i<=rt;i++)
for (int j=1;j<=K;j++)
if (s[j]>=bs[i].mark)
ans+=bs[i].bin[s[j]-bs[i].mark];
printf("%d\n",ans);
}
}
fclose(stdin);
fclose(stdout);
return 0;
}