「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 个操作。具体地,有以下两种操作:

  1. add l r d 把第 l l l 到第 r r r 个数都加上 d d d
  2. 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 1N,M105 1 ≤ l ≤ r ≤ N 1\leq l\leq r\leq N 1lrN 1 ≤ d ≤ 1 0 4 1\leq d \leq 10^4 1d104

思路

吐槽一下,这道题评 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,xtagi 就代表着当前块里有多少个数等于 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;
}
  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值