【PR #15】最小表示法/【PR #11】作业/QOJ1855. Minimal Cyclic Shift
【PR #15】最小表示法/【PR #11】作业/QOJ1855. Minimal Cyclic Shift
题意
有 \(n\) 个字符串 \(s_i\),第 \(i\) 个字符串长 \(a_i\)。
定义 \(f(s)\) 表示字符串 \(s\) 的最小循环移位的起始位置,具体解释见【PR #15】最小表示法。如果有多个最小循环移位取最小起始位置。
字符集大小 \(26\),所有字符串随机生成,问序列 \(\{f(s_1),f(s_2),\dots,f(s_n) \}\) 和 序列 \(\{f(s_n),f(s_1),\dots, f(s_{n-1})\}\) 期望有多少位置相同。
\(n,a_i \le 10^5\)。
思路
因为字符串都是随机的,所以我们可以算相邻两个字符串最小循环移位起始位置相同的期望。数值上等于概率。然后把 \(n\) 个期望相加就是答案。
问题变成快速求长度为 \(a,b\) 的两个字符串 \(f\) 相同的概率,总方案数就是 \(26^{n+m}\),所以求相同的方案数,然后除以总方案数就好。
\(g(f(a)=f(b))\) 的方案数就等于 \(\sum_{i=1}^{\min(a,b)} g(f(a)=i) \times g(f(b)=i)\)。其中这里 \(f(a)\) 表示长度为 \(a\) 的字符串的最小循环移位起始位置,\(g(x)\) 表示满足条件 \(x\) 的方案数。
如何求 \(g(f(a)=i)\)?考虑怎么求 \(g(f(a)=1)\)。要么 \(1\) 是唯一最小循环移位起始位置,要么 \(1\) 是最小的之一。
这么分类是因为对于不同 \(i\),\(i\) 是唯一最小循环移位起始位置的条件本质上是一样的,即方案数一样。设 \(h_{a,i}\) 表示长度为 \(a\) 的字符串只有唯一的最小循环移位,起始位置是 \(i\) 的方案数。因为 \(h_{a,i}=h_{a,j}\),所以不写 \(i\) 这一维。
如果存在多个最小循环移位,当且仅当原字符串有整循环节。可以枚举极小循环节长度,每个极小循环节要求只有唯一最小循环移位。这样就拆成因数个数个子问题了。
总方案数减去存在不唯一最小循环移位方案数,就是存在唯一最小循环移位方案数。
那么 \(g(f(a)=x)\) 就是 \(x\) 是唯一最小位置,加上存在多个最小位置,但是 \(x\) 是其中下标最小的,\(d\ge x\) 就是这么来的。
最后求 \(\sum_{x=1}^{\min(a_i,a_{i+1})} g(f(a_i)=x) \cdot g(f(a_{i+1})=x)\)。
对于 \(1 \le x \le a\),答案不同的 \(x\) 只有 \(a\) 的因数个数个。
所以把 \(a_i\) 和 \(a_{i+1}\) 的因数归并排序一下,对每一块算一次。按顺序枚举因数,\(g\) 只需要增加或者减少 \(O(1)\) 个 \(h\) 就可以了。所以复杂度是因数个数。
预处理因数和 \(h\)。时间复杂度是 \(O((n+q)因数个数+n\sqrt{n})\) 的。预处理因数当然可以不带根号,但是没必要。
code
注意特判 \(n=1\)。
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
	constexpr int N=1e5+7,mod=998244353,W=26,MAXN=1e5;
	int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
	void _add(int &a,int b) { a=add(a,b); }
	int mul(int a,int b) { return 1ll*a*b%mod; }
	void _mul(int &a,int b) { a=mul(a,b); }
	int n,a[N];
	int inv[N],invw[N],miw[N];
	int ksm(int a,int b=mod-2) {
		int s=1;
		while(b) {
			if(b&1) _mul(s,a);
			_mul(a,a);
			b>>=1;
		}
		return s;
	}
	vector<int> vec[N];
	int g[N];
	void init() {
		miw[0]=1;
		rep(i,1,MAXN) miw[i]=mul(miw[i-1],W);
		invw[MAXN]=ksm(miw[MAXN]);
		per(i,MAXN-1,0) invw[i]=mul(invw[i+1],W);
		inv[1]=1;
		rep(i,2,MAXN) inv[i]=mul((mod-mod/i),inv[mod%i]);
		rep(i,1,MAXN) {
			for(int j=1;j*j<=i;j++) {
				if(i%j==0) {
					vec[i].push_back(j);
					if(j*j!=i) vec[i].push_back(i/j);
				}
			}
			sort(vec[i].begin(),vec[i].end());
		}
		rep(i,1,MAXN) {
			g[i]=miw[i];
			for(int d : vec[i]) if(d^i) _add(g[i],mod-mul(g[d],d));
			_mul(g[i],inv[i]);
		}
	}
	int f[N];
	int ans;
	void main() {
		init();
		sf("%d",&n);
		if(n==1) {
			pf("%d\n",1);
			exit(0);
		}
		rep(i,1,n) sf("%d",&a[i]);
		a[0]=a[n];
		rep(i,1,n) {
			int f0=0,f1=0;
			for(int d : vec[a[i-1]]) _add(f0,g[d]);
			for(int d : vec[a[i]]) _add(f1,g[d]);
			vector<int> tmp;
			int it0=0,it1=0;
			int s0=vec[a[i-1]].size(),s1=vec[a[i]].size();
			while(it0<s0 && it1<s1) {
				int u=vec[a[i-1]][it0],v=vec[a[i]][it1];
				if(u==v) tmp.push_back(u), ++it0,++it1;
				else if(u<v) tmp.push_back(u), ++it0;
				else tmp.push_back(v), ++it1;
			}
			while(it0<s0) tmp.push_back(vec[a[i-1]][it0]), ++it0;
			while(it1<s1) tmp.push_back(vec[a[i]][it1]), ++it1;
			int la=0;
			for(int x : tmp) {
				_add(f[i],mul(mul(f0,f1),x-la));
				la=x;
				if(a[i-1]%x==0) _add(f0,mod-g[x]);
				if(a[i]%x==0) _add(f1,mod-g[x]);
			}
			_mul(f[i],mul(invw[a[i-1]],invw[a[i]]));
			_add(ans,f[i]);
		}
		pf("%d\n",ans);
	}
}
int main() {
	#ifdef LOCAL
	freopen("my.out","w",stdout);
	#endif
	wing_heart :: main();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/18716995

                
            
        
浙公网安备 33010602011771号