bzoj4542 大数 莫队算法

        (以下用[l,r]表示[l,r]这一段数字)对每一个后缀[i,n],求出这一段数对p取模后的值。那么对于某一段区间[l,r],如果满足[l,n]和[r+1,n]的值相同,那么显然有[l,r] mod p=10,实际上,是[l,r]*10^(n-r) mod p=0。那么离散化之后用莫队维护一下就好了。

       注意到当p=2或p=5的时候并不一定满足。因此需要特判。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100005
#define ll long long
using namespace std;

int n,m,cnt,a[N],c[N],blg[N],num[N]; ll p,ans,ot[N];
struct node{ ll x; int y; }s[N]; struct q_node{ int l,r,id; }b[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void work(){
	int i,x,y;
	for (i=1; i<=n; i++){
		s[i]=s[i-1];
		if (!(a[i]%p)){ s[i].x+=i; s[i].y++; }
	}
	m=read();
	while (m--){
		x=read(); y=read();
		printf("%lld\n",s[y].x-s[x-1].x-(ll)(x-1)*(s[y].y-s[x-1].y));
	}
}
bool cmp1(const node &u,const node &v){ return u.x<v.x; }
bool cmp2(const q_node &u,const q_node &v){
	return blg[u.l]<blg[v.l] || blg[u.l]==blg[v.l] && u.r<v.r;
}
void mdy(int x,int y){
	if (y>0){ ans+=num[c[x]]; num[c[x]]++;}
	else{ num[c[x]]--; ans-=num[c[x]]; }
}
int main(){
	scanf("%lld",&p); int i;
	char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar();
	for (; ch>='0' && ch<='9'; ch=getchar()) a[++n]=ch-'0';
	if (p==2 || p==5){ work(); return 0; }
	ll now=1;
	for (i=n; i; i--,now=now*10%p){
		s[i].x=(s[i+1].x+a[i]*now)%p; s[i].y=i;
	}
	s[++n].x=0; s[n].y=n;
	sort(s+1,s+n+1,cmp1);
	for (i=1; i<=n; i++){
		if (i==1 || s[i].x!=s[i-1].x) cnt++;
		c[s[i].y]=cnt;
	}
	m=read();
	for (i=1; i<=m; i++){
		b[i].l=read(); b[i].r=read()+1; b[i].id=i;
	}
	int sz=(int)sqrt(n)+1;
	for (i=1; i<=n; i++) blg[i]=(i-1)/sz;
	sort(b+1,b+m+1,cmp2);
	int l=1,r=1; num[c[1]]++;
	for (i=1; i<=m; i++){
		while (l<b[i].l) mdy(l++,-1); while (l>b[i].l) mdy(--l,1);
		while (r<b[i].r) mdy(++r,1); while (r>b[i].r) mdy(r--,-1);
		ot[b[i].id]=ans;
	}
	for (i=1; i<=m; i++) printf("%lld\n",ot[i]);
	return 0;
}


by lych

2016.4.22

你可能感兴趣的:(数学,离散化,莫队算法)