4542: [Hnoi2016]大数

4542: [Hnoi2016]大数

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 993   Solved: 360
[ Submit][ Status][ Discuss]

Description

  小 B 有一个很大的数 S,长度达到了 N 位;这个数可以看成是一个串,它可能有前导 0,例如00009312345
。小B还有一个素数P。现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也
是P 的倍数)。例如 S为0077时,其子串 007有6个子串:0,0,7,00,07,007;显然0077的子串007有6个子串都是素
数7的倍数。

Input

  第一行一个整数:P。第二行一个串:S。第三行一个整数:M。接下来M行,每行两个整数 fr,to,表示对S 的
子串S[fr…to]的一次询问。注意:S的最左端的数字的位置序号为 1;例如S为213567,则S[1]为 2,S[1…3]为 2
13。N,M<=100000,P为素数

Output

  输出M行,每行一个整数,第 i行是第 i个询问的答案。

Sample Input

11
121121
3
1 6
1 5
1 4

Sample Output

5
3
2
//第一个询问问的是整个串,满足条件的子串分别有:121121,2112,11,121,121。

HINT

 2016.4.19新加数据一组

Source

[ Submit][ Status][ Discuss]

对于原串,统计suf[i]:以i开头的后缀%p的结果,注意到p为素数
假如有suf[i] == suf[j] && i < j,那么子串[i,j) % p一定为0
因为gcd(p,10^k) != 1,那么原问题就能转换成统计区间内相同数字出现次数
记得每个询问区间右端点 + 1,离散一下,莫队解决

不过有特例。。当p == 2 || p == 5时,上述就不成立了。。
但是被这俩数整除的数特征很明显(只看最后一位就行了),因此用前缀和统计
原题数据并没有p == 2 || p == 5 。。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E6 + 10;
typedef long long LL;
const int A[5] = {0,2,4,6,8};
const int B[2] = {0,5};

struct Query{
	int l,r,flag,num; Query(){}
	Query(int l,int r,int flag,int num): l(l),r(r),flag(flag),num(num){}
	bool operator < (const Query &b) const
	{
		if (flag < b.flag) return 1;
		if (flag > b.flag) return 0;
		return r < b.r;
	}
}Q[maxn];

LL p,tot,suf[maxn],ans[maxn],a[maxn],sum[maxn],g[maxn];
int n,m,L = 1,R,Sqrt,cur = 1,cnt[maxn];
char s[maxn];
bool f1,f2;

void Inc(int k)
{
	++cnt[suf[k]];
	tot += 1LL*(cnt[suf[k]] - 1);
}

void Dec(int k)
{
	tot -= 1LL*(cnt[suf[k]] - 1);
	--cnt[suf[k]];
}

void Solve()
{
	for (int i = 1; i <= n; i++,sum[i] = sum[i-1],g[i] = g[i-1])
		if (f1)
		{
			for (int j = 0; j < 5; j++)
				if (A[j] == s[i] - '0')
					sum[i] += 1LL*i,g[i] += 1LL;
		}
		else
		{
			for (int j = 0; j < 2; j++)
				if (B[j] == s[i] - '0')
					sum[i] += 1LL*i,g[i] += 1LL;
		}
	cin >> m;
	for (int i = 1; i <= m; i++)
	{
		int l,r; scanf("%d%d",&l,&r);
		printf("%lld\n",sum[r] - sum[l-1] - 1LL*(l-1)*(g[r] - g[l-1]));
	}
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> p; LL t = 1;
	if (p == 2) f1 = 1;
	if (p == 5) f2 = 1;
	scanf("%s",s + 1);
	n = strlen(s + 1); 
	if (f1 || f2) {Solve(); return 0;} ++n;
	s[n] = '0'; Sqrt = sqrt(n);
	for (int i = n; i; i--)
	{
		LL x = s[i] - '0';
		suf[i] = suf[i+1] + x*t;
		suf[i] %= p;
		t *= 10LL; t %= p;
		a[++tot] = suf[i];
	}
	sort(a + 1,a + tot + 1);
	for (int i = 2; i <= tot; i++)
		if (a[i] != a[i-1])
			a[++cur] = a[i];
	tot = 0;
	for (int i = 1; i <= n; i++)
		suf[i] = lower_bound(a + 1,a + cur + 1,suf[i]) - a;
	cin >> m;
	for (int i = 1; i <= m; i++)
	{
		int l,r; scanf("%d%d",&l,&r);
		if (!f1 && !f2)
			Q[i] = Query(l,r+1,l % Sqrt == 0?l / Sqrt:l / Sqrt + 1,i);
		else Q[i] = Query(l,r,l % Sqrt == 0?l / Sqrt:l / Sqrt + 1,i);
	}
	sort(Q + 1,Q + m + 1);
	for (int i = 1; i <= m; i++)
	{
		while (R < Q[i].r) Inc(++R);
		while (Q[i].l < L) Inc(--L);
		while (L < Q[i].l) Dec(L),++L;
		while (Q[i].r < R) Dec(R),--R;
		ans[Q[i].num] = tot;
	}
	for (int i = 1; i <= m; i++) printf("%lld\n",ans[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值