2 条题解

  • 0
    @ 2026-9-26 10:05:00

    题解 P3538 【[POI2012]OKR-A Horrible Poem】

    题面

    判断字符串循环节最方便的是hashhash

    不会的请出门左转P3370

    我们先把字符串hashhash一遍

    如图,如果设循环节长度为33时,s1s1和s2s2的hashhash值是相等的

    所以只需要找最小的lenlen使得hash(l+len,r)=hash(l,r−len)hash(l+len,r)=hash(l,r-len)


    另外,循环节的长度的循环次数都一定是总长的约数

    我的做法是把总长除掉循环次数

    先把lenlen分解质因数

    (线性筛质数,并记录下每个数的最小质因子加速分解,这已经是常规操作了)

    因为最小循环节的倍数也是循环节

    所以从lenlen开始试除每个质因子并判断(你可以理解为lenlen的因子分为循环节的因子和循环次数的因子,要把循环次数的因子除掉)

    具体的看代码吧。。。


    代码

    #include <cstdio>
    #include <cctype>
    #include <vector>
    
    using namespace std;
    
    template<typename T> inline void read(T &x) {
    	x = 0; T k = 1; char in = getchar();
    	while (!isdigit(in)) { if (in == '-') k = -1; in = getchar(); }
    	while (isdigit(in)) x = x * 10 + in - '0', in = getchar();
    	x *= k;
    }
    
    typedef long long ll;
    
    const ll MOD = 1e9 + 7;
    const int N = 5e5 + 5;
    
    int n, m;
    ll g[N], hash[N], pow[N];// g记录最小质因子,pow存进制的整数次幂
    char s[N];
    bool vis[N];
    vector<ll> pri;
    
    // 线性筛
    inline void euler() {
    	for (ll i = 2; i <= n; ++i) {
    		if (!vis[i])
    			pri.push_back(i), g[i] = i;
    		for (int j = 0; j < pri.size() && pri[j] * i <= n; ++j) {
    			vis[pri[j]*i] = true, g[pri[j]*i] = pri[j];
    			if (i % pri[j] == 0)
    				break;
    		}
    	}
    }
    
    // 提取hash值
    inline ll calc(int l, int r) {
    	return ((hash[r] - hash[l-1] * pow[r-l+1]) % MOD + MOD) % MOD;
    }
    
    int main() {
    	read(n);
    	euler();
    	scanf("%s", s+1);
        // hash
    	for (int i = 1; i <= n; ++i)
    		hash[i] = (hash[i-1] * 29 + s[i] - 'a' + 1) % MOD;
    	
        // 预处理整数次幂
    	pow[0] = 1;
    	for (int i = 1; i <= n; ++i)
    		pow[i] = (pow[i-1] * 29) % MOD;
    
    	read(m);
    	while (m--) {
    		int l, r, len, ans;
    		read(l), read(r), ans = len = r - l + 1;
            // 一点点常数优化
    		if (calc(l+1, r) == calc(l, r-1)) {
    			puts("1");
    			continue;
    		}
            // 重点
    		while (len > 1) {
    			if (calc(l+ans/g[len], r) == calc(l, r-ans/g[len]))// 判断
    				ans /= g[len];// 除掉循环次数的因子
    			len /= g[len];//分解
    		}
    		printf("%d\n", ans);
    	}
        return 0;
    }
    
    • 0
      @ 2025-10-8 17:07:02
      #include <bits/stdc++.h>
      using namespace std;
      typedef unsigned long long ULL;
      const int N=5e5+10;
      int n, factor[N], prime[N], pr; bool v[N];
      void init()
      {
          pr=0; memset(v,0,sizeof(v));
          for(int i=2;i<=n;i++)
          {
              if(v[i]==0) prime[++pr]=i; factor[i]=i;
              for(int j=1;j<=pr && i*prime[j]<=n;j++)
              {
                  v[i*prime[j]]=1; factor[i*prime[j]]=prime[j];
                  if(i%prime[j]==0) break;
              }
          }
      }
      ULL f[N], d[N];
      char s[N];
      ULL Hash(int l, int r){return f[r]-f[l-1]*d[r-l+1];}
      bool check(int l, int r, int len){return Hash(l, r-len)==Hash(l+len, r);}
      int main()
      {
          scanf("%d%s", &n, s+1);
          init();
          d[0]=1; for(int i=1;i<=n;i++) d[i]=d[i-1]*131;
          for(int i=1;i<=n;i++) f[i]=f[i-1]*131+s[i];
          int q; scanf("%d", &q);
          while(q--)
          {
              int l, r, L; scanf("%d%d", &l, &r); L=r-l+1;
              for(int i=L; i>1; i=i/factor[i])
              {
                  if(check(l, r, L/factor[i])) L=L/factor[i];
              }
              printf("%d\n", L);
          }
          return 0;
      }
      
      • 1

      [POI 2012] OKR-A Horrible Poem可怕的诗

      信息

      ID
      4460
      时间
      20000ms
      内存
      128MiB
      难度
      7
      标签
      递交数
      86
      已通过
      21
      上传者