【bzoj4542】大数 莫队

9 篇文章 0 订阅

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=4542

【题解】

把n个后缀组成的数字全部对p取模。

若s[l] ~ s[n]的余数和s[r] ~ s[n]的余数相同,那么s[l] ~ s[r - 1]区间内的数字就是p的倍数(l < r)

证明:设k为s[r-1]%p,x=r-l,n为s[l] ~ s[r - 1]区间内的数字,k%p=a

则(k+n*10^x)%p=a,若gcd(10^x,p)=1,因为k%p=a,所以n%p=0

由上面的证明过程可以知道当p=2或5时不成立,我们需要特判一下:

当p=2或5时,用b[i]记录在i及左边满足(s[i]-'0')%p=0的数的个数,c[i]记录i及左边的答案。

那么对于询问[l,r]的答案就是(c[r]-c[l-1])-(b[r]-b[l-1])*(l-1),这个动手推一下就出来了。

然后回到p不是2或5的情况:

很显然,这是一个经典的问题:在[l,r+1]内有多少对相同的数。

另外,注意离散化的使用。

/*************
  bzoj 4542
  by chty
  2016.11.17
*************/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
#define FILE "read"
#define MAXN 100010
#define up(i,j,n) for(ll i=j;i<=n;i++)
#define down(i,j,n) for(ll i=j;i>=n;i--)
struct node{ll x,y,id;}q[MAXN];
ll p,n,m,now,block,ans[MAXN],a[MAXN],num[MAXN],b[MAXN],c[MAXN];
char ch[MAXN];
map<ll,ll>Map;
namespace Init{
	char buf[1<<15],*fs,*ft;
	inline char getc() {return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
	inline ll read(){
		ll x=0,f=1;  char ch=getchar();
		while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
		while(isdigit(ch))  {x=x*10+ch-'0';  ch=getchar();}
		return x*f;
	}
}
namespace Mo_Team{
	ll cnt[MAXN];
	bool cmp(node a,node b) {return a.x/block==b.x/block?a.y<b.y:a.x/block<b.x/block;}
	void add(ll x) {now+=cnt[x];  cnt[x]++;}
	void del(ll x) {cnt[x]--;  now-=cnt[x];}
	void work(){
		block=(ll)sqrt(n*1.0); sort(q+1,q+m+1,cmp);
		up(i,1,m)  q[i].y++;
		ll l=1,r=0;
		up(i,1,m){
			while(q[i].x>l)  del(a[l++]);
			while(q[i].x<l)  add(a[--l]);
			while(q[i].y>r)  add(a[++r]);
			while(q[i].y<r)  del(a[r--]);
			ans[q[i].id]=now;
		}
		up(i,1,m)  printf("%lld\n",ans[i]);
	}
}
void init(){
	p=Init::read();
	scanf("%s",ch+1);
	m=Init::read();  n=strlen(ch+1);  
	up(i,1,m){q[i].x=Init::read(); q[i].y=Init::read(); q[i].id=i;}
	ll mul=1;
	down(i,n,1){
		mul=mul*10%p;
		a[i]=(a[i+1]+(ch[i]-'0')*mul)%p;
		num[i]=a[i];
	}
	sort(num+1,num+n+1);
	up(i,1,n+1)  Map[num[i]]=i;
	up(i,1,n+1)  a[i]=Map[a[i]];
}
void solve(){
	if(p==2||p==5){
		up(i,1,n) if((ch[i]-'0')%p==0) {b[i]=b[i-1]+1;  c[i]=c[i-1]+i;}
		else {b[i]=b[i-1];  c[i]=c[i-1];}
		up(i,1,m) {ll l=q[i].x,r=q[i].y; printf("%lld\n",c[r]-c[l-1]-(b[r]-b[l-1])*(l-1));}
	}
	else Mo_Team::work();
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	init();
	solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值