1. 程式人生 > >【xsy1018】 小A的字母遊戲 擴展CRT

【xsy1018】 小A的字母遊戲 擴展CRT

continue 題目 exc lcm 發現 ans 擴展 long long ()

題目大意:有$n$個無限長的循環字符串,所謂循環字符串,就是由某一個子串重復疊加而成。現在想知道最早在哪一位,這n個字符串的那一位的字母相同。

數據範圍:$n≤30000$,答案$<2^{63}$。

不難發現,此題你只要對每個字母求一個最早都出現的位置,然後取一個$min$就可以了。

對於每個字母取一個最早大家都出現過的位置,這個顯然用擴展中國剩余定理去求就可以了。

註意此題中串長的lcm可能會爆long long 要用__int128

 1 #include<bits/stdc++.h>
 2 #define MM 30005
 3 #define L __int128
 4
using namespace std; 5 6 int id[MM][52]={0},len[MM]={0}; 7 int n; 8 int vis[128]={0}; 9 10 void exgcd(L a,L b,L &x,L &y){ 11 if(b==0){x=1; y=0; return;} 12 exgcd(b,a%b,y,x); 13 y-=a/b*x; 14 } 15 L inv(L x,L MOD){ 16 L a=0,b=0; 17 exgcd(x,MOD,a,b); 18 return
a; 19 } 20 L __lcm(L m1,L m2){return m1*m2/__gcd(m1,m2);} 21 void excrt(L &c1,L &m1,L c2,L m2){ 22 L gcd=__gcd(m1,m2),lcm=__lcm(m1,m2); 23 if((c2-c1)%gcd) {m1=0; return;} 24 L x=(inv(m1/gcd,m2/gcd)*((c2-c1)/gcd)%lcm*m1%lcm+c1+lcm)%lcm; 25 c1=x; m1=lcm; 26 } 27 28 int main(){
29 scanf("%d",&n); 30 for(int x=1;x<=n;x++){ 31 memset(vis,0,sizeof(vis)); 32 char c[60]; scanf("%s",c+1); 33 len[x]=strlen(c+1); 34 for(int i=1;i<=len[x];i++) vis[c[i]]=i; 35 36 for(int i=A;i<=Z;i++) id[x][i-A]=vis[i]; 37 for(int i=a;i<=z;i++) id[x][i-a+26]=vis[i]; 38 } 39 L ans=1e18;// ans=ans*ans; 40 L INF=ans; 41 for(int x=0;x<52;x++){ 42 int ok=1; 43 for(int i=1;i<=n;i++) if(id[i][x]==0) {ok=0; break;} 44 if(ok==0) continue; 45 L c=id[1][x],m=len[1]; 46 for(int i=2;i<=n;i++){ 47 L C=id[i][x],M=len[i]; 48 excrt(c,m,C,M); 49 if(m==0) break; 50 } 51 if(m==0) continue; 52 if(c==0) c=m; 53 ans=min(ans,c); 54 } 55 if(ans==INF) cout<<-1<<endl; 56 else{ 57 long long out=ans; 58 cout<<out<<endl; 59 } 60 }

【xsy1018】 小A的字母遊戲 擴展CRT