1 solutions

  • 0
    @ 2023-12-15 10:44:37

    难度:

    算法: 字符串哈希,质数,素数判断,筛法

    A Horrible Problem.

    给定一个字符串,mm 次询问,每次询问一个子串,求其最小循环节。

    记这个字符串为 ss,第 ii 个字符为 sis_i,以 aa 开头 bb 结尾的子串记为 sa,bs_{a,b}。令当前处理的字串为 sa,bs_{a,b},记 m=a+b1m=a+b-1

    不难得到,如果 cc 不等于 mm,则 ccsa,bs_{a,b} 的循环节的充分必要条件是 sa,bc=sa+c,bs_{a,b-c}=s_{a+c,b}。这个判断可以使用字符串哈希在 O(1)O(1) 的时间内完成。

    如果 ccs(a,b)s_{(a,b)} 的循环节,那么对于任意满足 ck,kmc|k,k|mkkkk 仍为 sa,bs_{a,b} 的循环节。

    那么记当前最优答案为 cc。将 mm 因式分解得到 m=p1r2p2r2...pxrxm=p_1^{r_2}p_2^{r_2}...p_x^{r_x},由于上面的性质有 ckc|k,那么令 c=p1t1p2t2...pxtxc=p_1^{t_1}p_2^{t_2}...p_x^{t_x}。现在的问题就是求出每一个 tt。假设我们现在正在处理 t1t_1。假设 t1=qt_1=q,那么有 mp1t1q\frac{m}{p_1^{t_1-q}}s(a,b)s_{(a,b)} 的循环节, mp1t1q+1\frac{m}{p_1^{t_1-q+1}} 不为 s(a,b)s_{(a,b)} 的循环节。这里没有二分的必要,可以直接暴力判断。依次求出每个 tt,最后通过 c=p1t1p2t2...pxtxc=p_1^{t_1}p_2^{t_2}...p_x^{t_x} 算出 cc 即可。

    对于质因数分解,可以用筛子预处理每个数的最小质因子。每次质因数分解可在 log(m)\log(m) 时间内完成。

    但是这题卡的很死,所以要卡一下常:

    1.对所有主函数以外的函数使用 inline。

    2.int 的计算应该比 ll 或者 ull 快。能用 int 的用 int。

    3.读入量太大了,使用快读。

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ll;
    inline int read(){
        int x=0;int ch=getchar();
        while (!isdigit(ch)&&(ch!=EOF)) ch=getchar();
        while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return x;
    }
    int n,f[500050],t,a,b,m,l,x,y,d,g,pr[100050],pc;
    ll h[500050],p[500050],r=1e9+7;
    char s[500050];
    inline bool ch(int k){
    	if(h[b]-h[a+k-1]==(h[b-k]-h[a-1])*p[k]) return true;
    	return false;
    }
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++){
    		char c;
    		while(1){
    			c=getchar();
    			if(c!=EOF) break;
    		}
    		s[i]=c;
    	}
    	t=read();
    	for(int i=2;i<=n;i++){
    		if(!f[i]) f[i]=i,pr[++pc]=i;
    		for(int j=1;j<=pc;j++){
    			if(i*pr[j]>n) break;
    			if(!f[i*pr[j]]) f[i*pr[j]]=pr[j];
    			if(i%pr[j]==0) break;
    		}
    	}
    	p[0]=1;
    	for(int i=1;i<=n;i++) p[i]=p[i-1]*r;
    	for(int i=1;i<=n;i++) h[i]=h[i-1]+s[i]*p[i];
    	while(t--){
    		a=read(),b=read();
    		m=b-a+1,l=m,d=m,g=m;
    		while(m>1){
    			x=f[m],y=0;
    			while(f[m]==x){
    				m/=f[m];
    				y++;
    			}
    			d=g;
    			for(int i=1;i<=y;i++){
    				d/=x;
    				if(ch(d)) l/=x;
    				else break;
    			}
    		}
    		printf("%d\n",l);
    	}
    	return 0;
    }
    
    • 1

    Information

    ID
    40
    Time
    1200ms
    Memory
    512MiB
    Difficulty
    9
    Tags
    # Submissions
    15
    Accepted
    2
    Uploaded By