1. 程式人生 > 實用技巧 >BJTU 1867. try a try, ac is OK

BJTU 1867. try a try, ac is OK

題意:求一個數字串在模p意義下,子串表示的數等於m的個數(p為質數)

暴力想法:O(n^2)列舉子串,O(n^2)預處理每個串代表的數字,O(1)判斷

預處理空間不夠,改為O(n)預處理每個字尾f[i],需要的串[i,j)即為\(\dfrac{f[i]-f[j]}{10^{n-j+1}}\)

也就是我們需要

\(\dfrac{f_i-f_j}{10^{n-j+1}}\equiv m\mod p\)

同乘\(10^{n-j+1}\)後移項,左側是僅含有i的式子,右側是僅含有j的式子

\(f_i\equiv 10^{n-j+1}m+f_j\mod p\)

開桶存f[i]%p出現次數,O(1)統計,複雜度O(n),非常美好

然後這樣就Wrong Answer

舉個例子:

\(1\not\equiv2\mod 5\)

同乘10

\(10\equiv20\mod5\)

也就是同乘性確實成立,但我一直理解成充要條件了,明知沒有“同除性”這樣的東西,也就說明了它只是充分的(對於一個對的式子)

怎麼才能充要呢?當乘的數\(\perp\)p即可

所幸p是質數,需要乘的數只是10的k次冪,只含有2或5,所以特判一下2或5即可(這倆數正好也很好特判)

哎..

#include<bits/stdc++.h>

using namespace std;

inline int rd(){
	int ret=0,f=1;char c;
	while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
	while(isdigit(c))ret=ret*10+c-'0',c=getchar();
	return ret*f;
}
#define pc putchar
#define space() pc(' ')
#define nextline() pc('\n')
void pot(int x){if(!x)return;pot(x/10);pc('0'+x%10);}
void out(int x){if(!x)pc('0');if(x<0)pc('-'),x=-x;pot(x);}

typedef long long ll;

const int MAXN = 500005;

char S[MAXN];
int f[MAXN],b[MAXN],s[MAXN];
int n,p,m;

int sh[MAXN];

int mul(int x,int y){
    ll xx=x,yy=y;
    ll ret=(xx*yy)%p;
    return (int)ret;   
}

void spsolve(){
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(s[i]%p==m) ans+=i;
    }
    cout<<ans;
}

int main(){
    scanf("%s",S+1);
    p=rd();m=rd();
    n=strlen(S+1);
    for(int i=1;i<=n;i++) s[i]=S[i]-'0'; 
    if(p==5||p==2) return spsolve(),0;
    sh[0]=1;
    for(int i=1;i<=n+1;i++) sh[i]=(sh[i-1]*10)%p;
    ll ans=0;
    for(int i=n;i>=1;i--)
        f[i]=(f[i+1]+(sh[n-i]*s[i])%p)%p;
    for(int i=1;i<=n+1;i++){
        ans+=1ll*b[(f[i]+mul(m,sh[n-i+1]))%p];
        b[f[i]%p]++;
    }
    cout<<ans<<endl;
	return 0;
}