UOJ #35 后缀排序 哈希做法
题面
题解
后缀数组当然可以
这里用哈希做
首先排序的问题在哪里
在于比较两个后缀的复杂度是O(length)的
但是我们可以通过找LCP来优化比较
我们二分两个串的LCP的长度 然后通过hash值判断是否相同
这样我们可以在$O(\log l)$的时间内算出两个串的LCP长度
所以排序的复杂度变成$O(n \log ^2 n )$
然后求出连续两个后缀的LCP还是用二分的方法做 复杂度$O(n \log n)$
总的复杂度$O(n \log ^2 n)$
Code
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 5 ll read(){ 6 ll x=0,f=1;char c=getchar(); 7 while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();} 8 while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();} 9 return x*f; 10 } 11 12 const int maxn=1000100; 13 const int mod=1e9+7; 14 int mu[maxn],pr[maxn],cnt; 15 bool isp[maxn]; 16 int fib[maxn],g[maxn],G[maxn],invG[maxn]; 17 int fpw[maxn][3]; 18 19 inline int ksm(int a,int b){ 20 int ret=1; 21 for(int i=0;i<=30;i++){ 22 if(b&1) ret=ret*1ll*a%mod; 23 a=a*1ll*a%mod; 24 b=b>>1; 25 if(b==0) return ret; 26 } 27 } 28 29 inline int inv(int a){ 30 return ksm(a,mod-2); 31 } 32 33 int main(){ 34 #ifdef LZT 35 freopen("in","r",stdin); 36 #endif 37 mu[1]=1; 38 memset(isp,1,sizeof(isp)); 39 for(int i=2;i<=1000000;i++){ 40 if(isp[i]){ 41 pr[++cnt]=i; 42 mu[i]=-1; 43 } 44 for(int j=1;j<=cnt && i*pr[j]<=1000000;j++){ 45 isp[i*pr[j]]=0; 46 if(i%pr[j]==0) break; 47 mu[i*pr[j]]=-mu[i]; 48 } 49 } 50 51 fib[1]=1; 52 for(int i=2;i<=1000000;i++) 53 fib[i]=(fib[i-1]+fib[i-2])%mod; 54 for(int i=1;i<=1000000;i++){ 55 fpw[i][0]=inv(fib[i]); 56 fpw[i][1]=1; 57 fpw[i][2]=fib[i]; 58 } 59 for(int i=1;i<=1000000;i++) 60 g[i]=1; 61 for(int i=1;i<=1000000;i++) 62 for(int j=i;j<=1000000;j+=i) 63 g[j]=g[j]*1ll*fpw[i][mu[j/i]+1]%mod; 64 G[0]=1; 65 for(int i=1;i<=1000000;i++) 66 G[i]=G[i-1]*1ll*g[i]%mod; 67 68 invG[0]=1; 69 for(int i=1;i<=1000000;i++) 70 invG[i]=inv(G[i]); 71 72 int tc=read(); 73 while(tc--){ 74 int n=read(),m=read(); 75 int j; 76 int ans=1; 77 for(int i=1;i<=min(n,m);i=j+1){ 78 j=min(n/(n/i),m/(m/i)); 79 ans=ans*1ll*ksm(G[j]*1ll*invG[i-1]%mod,(n/i)*1ll*(m/i)%(mod-1))%mod; 80 } 81 printf("%d\n",ans); 82 } 83 84 return 0; 85 }