LOJ #2563. 「SDOI2018」反回文串(Pollard-Rho , Mobius反演)

题目
发现题目就是求循环某位数后是回文串的长度为n的串的数量。
考虑长度为n的串,最小循环节出现了 k k k次,串被分为了 k k k块。
那么循环 n k \frac nk kn次后又会得到一个回文串。
有没有可能比 n k \frac nk kn更小呢?
发现这个等价于求无循环节(循环节为字符串长度)的回文串循环多少次后还是回文串。
发现循环u次后,串会变成一个长度为 2 u 2u 2u的回文串+一个长度为 n − 2 u n-2u n2u的回文串。
然后(假设)这整个串回文,所以可以得到 v v v个长度为 2 u 2u 2u的回文串和一个长度为 n − 2 v u n-2vu n2vu的回文串满足: n − 2 v u < 2 u n-2vu < 2u n2vu<2u,然后这两个和之前 2 u 2u 2u n − 2 u n-2u n2u的情况类似,所以可以一直递归下去。如果 n − 2 v u = 0 n-2vu=0 n2vu=0了,那么这个串就有长度为 v v v的循环节,矛盾。
但是等等,如果 v = 1 v = 1 v=1,即 2 u = n 2u = n 2u=n,这时就没有循环节了,但是需要满足串长为偶数。

所以要么最少循环 n k \frac nk kn次,要么循环 n 2 k \frac n{2k} 2kn次( 2 ∣ n k 2 | \frac nk 2kn),就会又得到一个回文串。
我们把每个回文串最少循环几次会得到回文串作为一个回文串的价值,那么所有回文串的价值之和刚好就不重不漏的计算了符合条件的串的个数。

枚举最小循环节长度 k k k
d ( k ) = k 1 + [ 2 ∣ k ] d(k) = \frac k{1+[2|k]} d(k)=1+[2k]k
f ( k ) = 最 小 循 环 节 长 度 为 k 的 串 个 数 f(k) = 最小循环节长度为k的串个数 f(k)=k
a n s = ∑ k ∣ n d ( k ) ∗ f ( k ) ans = \sum_{k|n} d(k)*f(k) ans=knd(k)f(k)
f f f好像是不好求的
由于 ∑ k ∣ n f ( k ) = ∣ Σ ∣ ⌈ n / 2 ⌉ , ∣ Σ ∣ 为 字 符 集 大 小 \sum_{k|n} f(k) = |\Sigma|^{\lceil n/2 \rceil} , |\Sigma|为字符集大小 knf(k)=Σn/2,Σ
所以可以莫比乌斯反演 f ( n ) = ∑ k ∣ n ∣ Σ ∣ ⌈ k / 2 ⌉ μ ( n k ) f(n) = \sum_{k|n} |\Sigma|^{\lceil k/2 \rceil} \mu(\frac nk) f(n)=knΣk/2μ(kn)
带入得到:
a n s = ∑ k ∣ n d ( k ) ∑ p ∣ k ∣ Σ ∣ ⌈ p / 2 ⌉ μ ( k p ) ans = \sum_{k|n}d(k) \sum_{p|k} |\Sigma|^{\lceil p/2 \rceil} \mu(\frac kp) ans=knd(k)pkΣp/2μ(pk)
把不好惹的放外面:
a n s = ∑ p ∣ n ∣ Σ ∣ ⌈ p / 2 ⌉ ∑ k ∣ n p d ( k p ) μ ( k ) ans = \sum_{p|n}|\Sigma|^{\lceil p/2 \rceil} \sum_{k|\frac np} d(kp)\mu(k) ans=pnΣp/2kpnd(kp)μ(k)
d ( k p ) d(kp) d(kp)好像也不好惹
考虑把 d ( k p ) d(kp) d(kp) k k k p p p其中一个提出来。
考虑到 d ( ) d() d()是不好惹的,我们最好把他扔出来,
尝试 d ( k p ) = k d ( p ) d(kp) = kd(p) d(kp)=kd(p)
k p kp kp为奇数时成立,
k p kp kp为偶数且 p p p为偶数时也成立,
k p kp kp为偶数且 p p p为奇数时好像不成立。
因为: p p p为奇数时且 k p kp kp为偶数时, n p \frac np pn为偶数。
∑ k ∣ n p d ( k p ) μ ( k ) = p ∑ k ∣ n p d ( k ) μ ( k ) = p ∑ k ∣ n p   a n d   k   i s   o d d d ( k ) μ ( k ) + d ( 2 k ) μ ( 2 k ) = p ∑ k ∣ n p   a n d   k   i s   o d d d ( k ) μ ( k ) + d ( k ) ( − μ ( k ) ) = 0 \sum_{k|\frac np} d(kp)\mu(k) = p\sum_{k|\frac np} d(k)\mu(k) = p\sum_{k|\frac np\ and \ k \ is \ odd} d(k)\mu(k) + d(2k)\mu(2k) =p\sum_{k|\frac np\ and \ k \ is \ odd} d(k)\mu(k) + d(k)(-\mu(k)) = 0 kpnd(kp)μ(k)=pkpnd(k)μ(k)=pkpn and k is oddd(k)μ(k)+d(2k)μ(2k)=pkpn and k is oddd(k)μ(k)+d(k)(μ(k))=0
所以贡献为0,不用管。
所以答案 a n s = ∑ p ∣ n ∣ Σ ∣ ⌈ p / 2 ⌉ d ( p ) ∑ k ∣ n p k μ ( k ) ans = \sum_{p|n}|\Sigma|^{\lceil p/2 \rceil}d(p) \sum_{k|\frac np} k\mu(k) ans=pnΣp/2d(p)kpnkμ(k)
然后 ∑ k ∣ n k μ ( k ) \sum_{k|n}k\mu(k) knkμ(k)可以发现 = ∑ p i 为 n 的 质 因 子 ( 1 − p i ) =\sum_{p_i为n的质因子}(1-pi) =pin(1pi)
然后可以把 n n n p o l l a r d − r h o pollard-rho pollardrho一下,算一下最多只有 2 ( 2 ∗ 3 ∗ 5 ∗ . . . ∗ 47 = 1 0 17 ) 2^{}(2*3*5*...*47=10^{17}) 2(235...47=1017)个约数,
然后就可以算了。

然后你(可能)会发现你T了,下组数据一测发现Pollard-Rho的时间耗费是0ms( luogu \texttt{luogu} luogu的毒瘤卡常Pollard-Rho了解一下,350组数据跑1s),计算答案时的 ∣ Σ ∣ ⌈ p / 2 ⌉ |\Sigma|^{\lceil p/2 \rceil} Σp/2的快速幂成了最耗时的部分,原因?在快速幂中用了 m o d    L o n g L o n g \mod LongLong modLongLong才需要的快速乘。模数为 i n t int int就直接转LL模就行了,快了(至少)4倍(也可以理解为快速乘有4倍以上的常数)。

AC Code:

#include
#define LL long long
using namespace std;

LL n,k,mod;
#define il inline 
il LL mul(LL a,LL b,LL p = mod){ 
	a=(a%p+p)%p,b=(b%p+p)%p;
	return (((a*b)-(LL)((long double)a*b/p)*p)%p+p)%p; }
il LL Pow(LL base,LL k,LL p = mod){
	LL ret=1;
	for(;k;k>>=1,base=mul(base,base,p))
		if(k&1)
			ret=mul(ret,base,p);
	return ret;
}
il int ksm(int base,LL k){
	int ret = 1;
	for(;k;k>>=1,base=1ll*base*base%mod)
		if(k&1)
			ret=1ll*ret*base%mod;
	return ret;
}
il LL gcd(LL a,LL b){ return !b ? a : gcd(b,a%b); }

namespace Pollard_Rho{
	int base[5]={2,3,7,31,61};
	bool Miller_Rabin(LL x){
		for(int i=0;i<5;i++) if(x == base[i]) return 1;
		LL res = x-1 , k = 0;
		for(;!(res&1);res>>=1,k++);
		for(int i=0;i<5;i++){
			LL pre = Pow(base[i],res,x) , now;
			for(int t=k;t--;swap(now,pre))
				if((now=mul(pre,pre,x))==1 && pre!=1 && pre!=x-1)
					return 0;
			if(pre!=1) return 0;
		}
		return 1;
	}
	LL Rho(LL x,LL c){
		LL i=1,j=0,sum=1,a=rand() % (x-1) + 1,b=a,d=1;
		for(;d==1;){
			sum = mul(sum , abs((a = (mul(a,a,x) + c) % x) - b) , x);
			if(++ j == i) i<<=1,b=a,d = gcd(sum , x);
			if(!(j&1023)) d = gcd(sum , x);
		} 
		return d == x ? Rho(x,c+1) : d;
	}
	map<LL,int>mp;
	void Pollard(LL x){
		if(x == 1) return;
		if(Miller_Rabin(x)){
			mp[x]++;
			return;
		}
		LL tmp = Rho(x,3);
		Pollard(tmp) , Pollard(x/tmp);
	}
	vector<pair<LL,int> >Solve(LL x){
		mp.clear(),Pollard(x);
		vector<pair<LL,int> > ret;
		for(auto u:mp) ret.push_back(u);
		return ret;
	}
}

vector<pair<LL,int> >ar;
LL ans = 0;
void dfs(int now,LL num,int sum){
	if(now == ar.size()){
		LL p = n/num;
		if((p&1) && !(num&1)) return;
		ans = (ans + ((p&1)?p%mod:p/2%mod) * ksm(k % mod,(p+1)/2) % mod * sum) % mod;
		return;
	}
	LL sn = 1;
	for(int i=0;i<=ar[now].second;i++,sn*=ar[now].first)
		if(i) dfs(now+1,num*sn,sum * 1ll * (mod+1-ar[now].first%mod) % mod);
		else dfs(now+1,num*sn,sum);
}

int main(){
	int T;
	for(scanf("%d",&T);T--;){
		scanf("%lld%lld%lld",&n,&k,&mod);
		ar = Pollard_Rho::Solve(n);
		ans = 0;
		dfs(0,1,1);
		printf("%lld\n",ans);
	}
}

你可能感兴趣的:(数论,奇巧淫技,性质分析,pollard-rho)