CF121E Lucky Array

题意

给定序列 aa,定义幸运数为各位数字只含有 4,74, 7 的数字,例如 44744474 是幸运数,而 41774177 则不是。现在有 mm 次操作,每次可能是将 al,al+1,,ara_l, a_{l+1}, \cdots, a_r 全部加上 dd,或者询问 al,al+1,,ara_l, a_{l+1}, \cdots, a_r 中有几个幸运数。

解法

考虑可以分块解决。

但是分块的难点在于操作 11,也就是区间加法。

显然如果我们暴力对 al,al+1,,ara_l, a_{l+1}, \cdots, a_r 全部暴力加上 dd 并且更新块的信息,复杂度是 O(n)O(n) 的,那么极限复杂度 O(nm)O(n \cdot m),显然无法通过。

那么我们考虑对所有整块都记录一个 plusiplus_i 表示这个块曾经被加上了 plusiplus_i,类似线段树上的懒标记。然后每一个快不再记录幸运数数量,而是对于每个块 fi,jf_{i,j} 记录的是块 iijj 出现次数,幸运数预处理打表即可。

代码:

// 省略火车头
#include <string>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;

constexpr int N = 1e5 + 5, M = 705, Q = 1e4 + 5;
int a[N], len;
int plusb[N];
int f[M][Q];

inline int get(register const int& x)
{
	return (x - 1) / len;
}

inline int lplace(register const int& x)
{
	return x * len + 1;
}

inline int rplace(register const int& x)
{
	return lplace(x + 1) - 1;
}

int p[] = { 4, 7, 44, 47, 74, 77, 444, 447, 474, 477, 744, 747, 774, 777, 4444, 4447, 4474, 4477, 4744, 4747, 4774, 4777, 7444, 7447, 7474, 7477, 7744, 7747, 7774, 7777 };
bool flag[N];

int main()
{
	register int n, m;
	scanf("%d%d", &n, &m);
	len = sqrt(n);
	for (register int i(0); i < 30; i = -~i) flag[p[i]] = true;
	for (register int i(1); i <= n; i = -~i) scanf("%d", &a[i]);
	for (register int i(0); i <= len; i = -~i)
	{
		register int lp(lplace(i)), rp(rplace(i));
		for (register int j(lp); j <= rp; j = -~j)
		{
			f[i][a[j]]++;
		}
	}
	while (m--)
	{
		string ch;
		cin >> ch;
		if (ch[0] == 'a')
		{
			register int l, r, d;
			scanf("%d%d%d", &l, &r, &d);
			register int ls(get(l)), rs(get(r));
			if (ls == rs)
			{
				for (register int i(l); i <= r; i = -~i)
				{
					f[get(i)][a[i]]--;
					a[i] += d;
					f[get(i)][a[i]]++;
				}
			}
			else
			{
				register int lp = lplace(ls + 1), rp = rplace(rs - 1);
				for (register int i(l); i < lp; i = -~i)
				{
					f[get(i)][a[i]]--;
					a[i] += d;
					f[get(i)][a[i]]++;
				}
				for (register int i(rp + 1); i <= r; i = -~i)
				{
					f[get(i)][a[i]]--;
					a[i] += d;
					f[get(i)][a[i]]++;
				}
				register int nl(get(lp)), nr(get(rp));
				for (register int i(nl); i <= nr; i++)
				{
					plusb[i] += d;
				}
			}
		}
		else
		{
			register int sum = 0, l, r;
			scanf("%d%d", &l, &r);
			register int ls(get(l)), rs(get(r));
			if (ls == rs)
			{
				for (register int i(l); i <= r; i = -~i)
				{
					sum += flag[a[i] + plusb[get(i)]];
				}
			}
			else
			{
				register int lp = lplace(ls + 1), rp = rplace(rs - 1);
				for (register int i(l); i < lp; i = -~i) sum += flag[a[i] + plusb[get(i)]];
				for (register int i(rp + 1); i <= r; i = -~i) sum += flag[a[i] + plusb[get(i)]];
				register int nl(get(lp)), nr(get(rp));
				for (register int i(nl); i <= nr; i = -~i)
				{
					for (register int j(0); j < 30; j = -~j)
					{
						if (plusb[i] > p[j]) continue;
						sum += f[i][p[j] - plusb[i]];
					}
				}
			}
			printf("%d\n", sum);
		}
	}
	return 0;
}

限时 44 秒,我的跑了 39623962 毫秒,如果把火车头删了的话那就可能要超时。

AC 记录。

posted @   HappyBobb  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示