题意:统计在长度为n的序列中有多少个连续子序列,使得素数p能够整除它。
解法:统计该序列的低位的前i位(mod)p的大小sum[i],则任意序列f(i,j)=(sum[i]-sum[j])/(10^j),即f(i,j)*(10^j)=sum[i]-sum[j],其中j<=i。左右两边同(mod)p
1.如果(p,10)=1,则
f(i,j)=0(modp)<=>(sum[i]−sum[j])=0(modp)
因此统计以第i位结尾的连续子序列个数,只要统计前i-1个sum值中有多少个与其相等即可!用cnt[i]记录i出现的次数。
2.如果p==2||p==5,则可以通过结尾数的性质判断是否被其整除
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
#define lowbit(x) x&(-x)
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
#define rad 10000
const ll maxn = 100000+10;
ll n,s,w,q;
ll a[maxn],b[maxn],sum[maxn];
map<ll,ll> cnt;
ll ans;
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%lld%lld%lld%lld",&n,&s,&w,&q)!=EOF){
if(!n||!s||!w||!q) break;
for(ll i=0;i<n;i++){
a[i]=(s/7)%10;
if(s%2==0) { s/=2; }
else { s=(s/2)^w; }
}
ll x=0;
ans=0;
if(q==2||q==5){
for(ll i=0;i<n;i++){
if(a[i]) x++;
if(a[i]%q==0) ans+=x;;
}
printf("%lld\n",ans);
continue;
}
for(ll i=0;i<n;i++) b[i]=a[n-1-i];
for(ll i=0;i<n;i++) a[i]=b[i];
ll base=1;
cnt.clear();
for(ll i=0;i<n;i++){
if(i==0) sum[i]=a[i]%q;
else{
sum[i]=(sum[i-1]+(a[i]*base)%q)%q;
}
base=(base*10)%q;
}
cnt[0]++;
for(ll i=0;i<n;i++){
if(a[i]){
ans+=cnt[sum[i]];
}
cnt[sum[i]]++;
}
printf("%lld\n",ans);
}
return 0;
}