[HNOI2016]大数 题解

本文解析了一个关于整数数组a中,对于质数p,如何找到满足[l,r]=∑a[i]*10^(r-i) ≡ 0 (mod p)的[l,r]区间,并利用莫队技巧转化为求区间内数字值相同的对数。通过预处理和逆元计算简化问题,最终给出C++代码实现。
摘要由CSDN通过智能技术生成
题面

在这里插入图片描述
在这里插入图片描述

样例
11 
121121 
3 
1 6 
1 5 
1 4
5
3
2
分析

易知 [ l , r ] = ∑ a [ i ] ∗ 1 0 r − i ≡ 0   ( m o d   p ) [l,r]=\sum a[i]*10^{r-i}\equiv 0\ (mod\ p) [l,r]=a[i]10ri0 (mod p)

  1. p p p 10 10 10 不互质,又因为 p p p 为质数,所以 p = 2 p=2 p=2 p = 5 p=5 p=5,这种情况 [ l , r ] ≡ a [ 1 ] ≡   0   ( m o d   p ) [l,r]\equiv a[1]\equiv\ 0\ (mod\ p) [l,r]a[1] 0 (mod p), 则只要 p ∣ a [ 1 ] p|a[1] pa[1] 即可。

  2. 反之,发现原来的式子不好往下延展,不妨令 p r e [ i ] = [ i , n ] pre[i]=[i,n] pre[i]=[i,n] 的数值(套路)。则 [ i , j ] = ( p r e [ i ] − p r e [ j + 1 ] ) / 1 0 j − i + 1 ≡ 0   ( m o d   p ) [i,j]=(pre[i]-pre[j+1]) / 10^{j-i+1} \equiv 0\ (mod\ p) [i,j]=(pre[i]pre[j+1])/10ji+10 (mod p)

    由于 1 0 i − j 10^{i-j} 10ij 的逆元等于 ( 1 0 j − i + 1 ) p − 2 (10^{j-i+1})^{p-2} (10ji+1)p2,一定与 p p p 互质,所以 p r e [ i ] − p r e [ j + 1 ] ≡ 0   ( m o d   p ) pre[i]-pre[j+1]\equiv 0\ (mod\ p) pre[i]pre[j+1]0 (mod p),即求一个区间内有多少对数字的值相同。这就是莫队的板子了。(注意询问的 r r r 要加 1 1 1

Code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#define LL long long
using namespace std;
const int MAXN = 2e5 + 5;
int p, m, t, a[MAXN], lsh[MAXN], f, l = 1, r = 0, c[MAXN], n, pre[MAXN];
char s[MAXN];
LL ans[MAXN], res;
struct Node {
	int X, Y, pos;
	bool operator < (const Node P) const { return X / t != P.X / t ? X < P.X : Y < P.Y; }
}query[MAXN];
void Add(int x, int F) {
	if(f) c[a[x]] ++, F ? res += (a[x] % p == 0) * (r - l + 1) : res += c[a[x]];
	else res += c[pre[x]], c[pre[x]] ++; 
}
void Del(int x, int F) {
	if(f) F ? res -= (a[x] % p == 0) * (r - l + 1 + 1) : res -= c[a[x]], c[a[x]] --;
	else c[pre[x]] --, res -= c[pre[x]];
}
int main() {
	int qwq = 1;
	scanf("%d%s%d", &p, s + 1, &m); n = strlen(s + 1); t = sqrt(n);
	for(int i = 1; i <= n; i ++) a[i] = s[i] - '0';
	for(int i = n; i >= 1; i --) pre[i] = (LL)(pre[i + 1] + (LL)qwq * a[i]) % p, qwq = ((LL)qwq * 10) % p, lsh[i] = pre[i];
	for(int i = 1; i <= m; i ++) scanf("%d%d", &query[i].X, &query[i].Y), query[i].Y ++, query[i].pos = i;
	sort(query + 1, query + 1 + m);
	sort(lsh + 1, lsh + 2 + n);
	int len = unique(lsh + 1, lsh + 2 + n) - lsh - 1; 
	for(int i = 1; i <= n + 1; i ++) pre[i] = lower_bound(lsh + 1, lsh + 1 + len, pre[i]) - lsh;
	if(p == 2 || p == 5) f = 1;
	for(int i = 1; i <= m; i ++) {
		if(f) query[i].Y --;
		while(l < query[i].X) Del(l ++, 0);
		while(l > query[i].X) Add(-- l, 0);
		while(r < query[i].Y) Add(++ r, 1);
		while(r > query[i].Y) Del(r --, 1);
		ans[query[i].pos] = res;
	}
	for(int i = 1; i <= m; i ++) printf("%lld\n", ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值