「Lucky Array」Solution
简述题意
定义幸运数为只包含 4 4 4 和 7 7 7 这两个数字的数。例如 47 47 47, 744 744 744, 4 4 4 都是幸运数字,但 5 , 16 , 467 5,16,467 5,16,467 不是。
给定长度为 n n n 的数组,进行 m m m 个操作。具体地,有以下两种操作:
add l r d
把第 l l l 到第 r r r 个数都加上 d d d;count l r
统计第 l l l 到第 r r r 个数有多少个幸运数字。
保证所有数操作前后都不超过 1 0 4 10^4 104。
请你编一个程序来执行这些操作。
1 ≤ N , M ≤ 1 0 5 1\leq N,M\leq 10^5 1≤N,M≤105, 1 ≤ l ≤ r ≤ N 1\leq l\leq r\leq N 1≤l≤r≤N, 1 ≤ d ≤ 1 0 4 1\leq d \leq 10^4 1≤d≤104。
思路
吐槽一下,这道题评 2400 就算了,居然还是一道 Div1 \text{Div1} Div1 的 E E E,不可思议。
注意到,所有数操作前后不超过
1
0
4
10^4
104,那么这也就意味着,修改操作的数量一定小于等于
1
0
4
10^4
104。如果先预处理出
30
30
30 个幸运数,每次暴力修改,再加上树状数组维护区间和,最坏时间复杂度是
1
0
4
×
m
l
o
g
n
10^4 \times mlogn
104×mlogn,但是它过了,而且跑得比正解快。
考虑正解,一眼分块。定义 c n t i , j cnt_{i,j} cnti,j 为第 i i i 个块中,元素 j j j 的数量。对于修改操作,两端的散块直接暴力,对于整块就打上 t a g tag tag 标记。同理,对于查询操作,两端的散块直接暴力统计,对于整块,我们枚举 30 30 30 个幸运数字,假设当前枚举的幸运数字为 x x x,当前块的整体加标记为 t a g i tag_i tagi,那么 c n t i , x − t a g i cnt_{i,x-tag_i} cnti,x−tagi 就代表着当前块里有多少个数等于 x x x,累加即可。
易得时间复杂度为
O
(
30
×
m
n
)
O(30 \times m\sqrt n)
O(30×mn),但是没有暴力跑的块。
顺便提一句,官解给的做法是维护每个数与最小的比它大的由
4
4
4 和
7
7
7 组成的数的差,如果差值小于
0
0
0 了就暴力重构线段树。个人感觉还不如暴力。
代码
给一份暴力的代码,加上 f r e a d fread fread 快读甚至能卡进 2 s 2s 2s。
#include<bits/stdc++.h>
#define lowbit(x) ((x) & (-x))
const int MAXN = 1e5 + 5;
using namespace std;
int n , q , bit[MAXN] , a[MAXN];
bool vis[MAXN];
namespace IO{
#define il inline
#define Re register
char B[1 << 15], *S = B, *T = B , obuf[1 << 15] , *p3 = obuf;
#define getchar() (S == T && (T = (S = B) + fread(B, 1, 1 << 15, stdin), S == T) ? EOF : *S++)
#define putchar(x) (p3-obuf<1 << 15)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>
il void read(Re item &x) {
char c(getchar());
x = 0;
int f(1);
while (c < '0' || c > '9') {if (c == '-') f = -1;c = getchar();}
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
x *= f;
}
il void readch(Re char &x) {
x = getchar();
while(x < 'a' || x > 'z') x = getchar();
}
template<typename item, typename ...Arg>
il void read(item &tmp, Arg& ...tmps) {read(tmp);read(tmps...);}
template<typename item>
il void write(Re item x) {
if (x < 0) {putchar('-') , x = -x;}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
inline void update(int k , int x) {
for (int i = k ; i <= n ; i += lowbit(i)) bit[i] += x;
}
inline int Sum(int k) {
int sum = 0;
for (int i = k ; i ; i -= lowbit(i)) sum += bit[i];
return sum;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
vis[4] = vis[7] = vis[44] = vis[47] = vis[74] = vis[77] = vis[444] = vis[447] = vis[474] = vis[477] = vis[744] = vis[747] = vis[774] = vis[777] = vis[4444] = vis[4447] = vis[4474] = vis[4477] = vis[4744] = vis[4747] = vis[4774] = vis[4777] = vis[7444] = vis[7447] = vis[7477] = vis[7474] = vis[7744] = vis[7747] = vis[7774] = vis[7777] = 1;
read(n , q);
for (int i = 1 ; i <= n ; i = -~i) {
read(a[i]);
if (vis[a[i]]) update(i , 1);
}
while(q --) {
char opt[10];
int l , r , d;
readch(opt[0]);
if (opt[0] == 'c') readch(opt[1]) , readch(opt[2]) , readch(opt[3]) , readch(opt[4]);
else readch(opt[1]) , readch(opt[2]);
read(l , r);
if (opt[0] ^ 'c') {
read(d);
for (int i = l ; i <= r ; i = -~i) {
if (vis[a[i]]) update(i , -1);
a[i] += d;
if (vis[a[i]]) update(i , 1);
}
} else {
write(Sum(r) - Sum(l - 1)) , putchar('\n');
}
}
fwrite(obuf , p3 - obuf , 1 , stdout);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】