*题解:P3538 [POI 2012] OKR-A Horrible Poem

原题链接

解析

如果你还不会利用哈希 \(O(n)\) 找循环节,那么你应该先去做 P4391

由于我们要求的是完整周期,所以循环节长度必定为所选片段长度的因子,于是可以很轻松地得到一个 \(O(q \sqrt n)\) 的做法。

设查询的字符串为 \(a\)。观察到若字符串 \(b\) 是一个完整周期,则对于所有满足 \(k \cdot \operatorname{len}(b) \mid \operatorname{len}(a)\)\(k\)\(b^k\) 也是一个完整周期。

\(c\) 为最短完整周期且 \(c^x=a\),很多题解到这里就直接开始对 \(x\) 分解质因数了,具体地,维护一个当前答案 \(ans\),初始为 \(\operatorname{len}(a)\),对于 \(\operatorname{len}(a)\) 的每个质因子 \(d\),判断 \(c[1,\dfrac{ans}{d}]\) 是否是一个完整周期,如果是,则令 \(ans \gets \dfrac{ans}{d}\)

但实际上还需要说明除的时候不会把 \(\operatorname{len}(c)\) 的质因子除掉。事实上,不存在一个字符串 \(d\) 使得 \(d\)\(a\) 的完整周期且 \(c\) 不是 \(d\) 的完整周期。使用周期引理可以轻松地证明这个命题。

周期引理:若 \(p,q\) 是字符串 \(S\) 的两个周期且 \(p + q \le \operatorname{len}(S) +\gcd(p,q)\),则 \(\gcd(p,q)\) 也是 \(S\) 的一个周期。

所以如果存在这样的 \(d\) 则必定存在一个长度为 \(\gcd(\operatorname{len}(c),\operatorname{len}(d))\) 的完整周期,原命题得证。

在线性筛素数的同时处理出每个数的最小质因子,我们就得到了一个时间复杂度 \(O(q \log n)\) 的做法。

代码

/*
*/
#include<bits/stdc++.h>
#define eps 0.000001
#define mid ((l + r) >> 1)
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<ll,int> pii;
const int N = 5e5 + 5,M = 3.2e4 + 5,base = 13331;
ull h[N],mi[N];
vector<int> v[N];
bool vis[N];
vector<int> pri;
int d[N];
void euler(){
	for(int i=2;i<N;i++){
		if(!vis[i]){
			d[i] = i;
			pri.push_back(i);
		}
		for(int j=0;j<pri.size() && i * pri[j] < N;j++){
			vis[i * pri[j]] = true;
			d[i * pri[j]] = pri[j];
			if(i % pri[j] == 0){
				break;
			}
		}
	}
}
ull get(int l,int r){
	return h[r] - h[l - 1] * mi[r - l + 1];
}
bool chk(int l,int r,int k){
	if(!k) return false;
	return get(l,r - k) == get(l + k,r);
}
int calc(int l,int r){
	int len = r - l + 1;
	int res = len,now = len; 
	while(now > 1){
		if(chk(l,r,res / d[now])){
			res /= d[now];
		}
		now /= d[now];
	}
	return res;
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out1.txt","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	euler();
	int n;
	cin>>n;
	string s;
	cin>>s;
	s = " " + s;
	mi[0] = 1;
	for(int i=1;i<s.size();i++){
		mi[i] = mi[i - 1] * base;
		h[i] = h[i - 1] * base + s[i];
	}
	int q;
	cin>>q;
	while(q--){
		int a,b;
		cin>>a>>b;
		cout<<calc(a,b)<<'\n';
	}
	return 0;
}
 
posted @ 2025-08-29 18:26  yutar  阅读(1)  评论(0)    收藏  举报