2019.03.07【SDOI2018】【洛谷P4607】【BZOJ5330】反回文串(莫比乌斯反演)(Pollard-Rho)

洛谷传送门

BZOJ传送门


解析:

首先稍有常识的人都知道这道题绝对不可能是字符串题。

这种只给串长和字符集大小的题目只可能是计数。

而计数方式有很多啊,DP,群论,生成函数,甚至这道题的做法,莫比乌斯反演。

看数据 D P DP DP和群论基本上就告别正解了,生成函数推不出通项公式也是白搭(而且字符串轮换循环的情况让生成函数也告别正解了)。

这TM谁一眼看得出是莫反啊。

我们先忘记我们知道这道题需要用莫比乌斯反演这件事,来尝试推理一遍。

什么?你不会莫比乌斯反演?敢来淦反演毒瘤省SDOI的题你说你不会莫比乌斯反演?

没办法了,洗洗睡吧


推♂倒:

我们需要求有多少个长度为 n n n,字符集大小为 k k k的字符串的某一个轮换是一个回文串。

首先,字符集大小为 k k k,长度为 n n n的总回文串个数,很显然是 k ⌊ n + 1 2 ⌋ k^{\lfloor\frac{n+1}{2}\rfloor} k2n+1

但是我们也很清楚,并不是所有回文串的每一个轮换都不同,比如 a a a a a aaaaa aaaaa就只能算作一个串。

考虑每个回文串有多少种不同的轮换,答案显然就是最小循环节长度。

但是每个回文串的所有轮换都要考虑一次吗?

我们发现 a b b a a b b a abbaabba abbaabba的所有轮换和 b a a b b a a b baabbaab baabbaab都相同。。。

也就是说,可能出现某个回文串的某个轮换是另一个不同的回文串。

日,感觉分析不下去了。

但是仔细一看,这种情况只有当回文串的循环节长度为偶数的时候才会发生。就是循环节转到一半的情况。

而且只可能是两个回文串平摊贡献,不然循环节长度还能再小。

换句话说,如果考虑所有回文串的不同轮换对答案的贡献,设某个回文串的最小循环节长度为 d d d,则设贡献函数 h ( d ) = ( d % 2 = = 0 ) ? d / 2 : d h(d)=(d\%2==0)?d/2:d h(d)=(d%2==0)?d/2:d

现在令 f ( d ) f(d) f(d)表示所有最小循环节长度为 d d d的回文串个数,则答案为 A n s = ∑ d ∣ n h ( d ) f ( d ) Ans=\sum_{d\mid n}h(d)f(d) Ans=dnh(d)f(d)

那么现在问题变成了求 f f f

注意这里要求是最小循环节,即不能有更小的循环节了。

日,感觉分析不下去了。

但是你回去想一想为什么我们会推到最小循环节就会发现:

g ( N ) = ∑ d ∣ N f ( d ) = k ⌊ N + 1 2 ⌋ g(N)=\sum_{d\mid N}f(d)=k^{\lfloor\frac{N+1}{2}\rfloor} g(N)=dNf(d)=k2N+1

其中 g ( N ) g(N) g(N)表示具有长度为 N N N的循环节的回文串总数。

你知道我要说什么吧

莫比乌斯反演!

f ( N ) = ∑ d ∣ N μ ( d ) g ( N d ) f(N)=\sum_{d\mid N}\mu(d)g(\frac{N}d) f(N)=dNμ(d)g(dN)

代回去得到:

A n s = ∑ d ∣ n h ( d ) ∑ t ∣ d μ ( d ) g ( d t ) = ∑ d ∣ n g ( d ) ∑ t d ∣ n h ( t d ) μ ( t ) \begin{aligned} Ans&=&&\sum_{d\mid n}h(d)\sum_{t\mid d}\mu(d)g(\frac{d}t)\\ &=&&\sum_{d\mid n}g(d)\sum_{td\mid n}h(td)\mu(t) \end{aligned} Ans==dnh(d)tdμ(d)g(td)dng(d)tdnh(td)μ(t)

我们现在想把 h ( t d ) h(td) h(td)中的一个常数 d d d提出来得到 t h ( d ) th(d) th(d),因为 ∑ t ∣ n d t μ ( t ) \sum_{t\mid\frac{n}d}t\mu(t) tdntμ(t)有一个很好用的性质,一会儿会用到。

现在考虑这个式子 h ( t d ) = t h ( d ) h(td)=th(d) h(td)=th(d)什么时候不成立

很显然就是 t t t为偶数但是 d d d为奇数数的时候。但是这时候可以把 d d d提出来。

考虑这时候这个式子的值是什么: d ∑ t ∣ n d h ( t ) μ ( t ) d\sum_{t\mid \frac{n}{d}}h(t)\mu(t) dtdnh(t)μ(t)

我们发现对于所有的偶数 t t t,如果 μ ( t ) ! = 0 \mu(t)!=0 μ(t)!=0,都有 h ( t ) μ ( t ) = − h ( t 2 ) μ ( t 2 ) h(t)\mu(t)=-h(\frac{t}2)\mu(\frac{t}2) h(t)μ(t)=h(2t)μ(2t)

换句话说, ∑ \sum 里面的所有奇数和偶数凑成一对后和为 0 0 0

特判掉所有这种情况就行了。

去掉上面的情况后我们直接来提出 t t t
A n s = ∑ d ∣ n g ( d ) h ( d ) ∑ t ∣ n d t μ ( t ) Ans=\sum_{d\mid n}g(d)h(d)\sum_{t\mid \frac{n}d}t\mu(t) Ans=dng(d)h(d)tdntμ(t)

S ( n ) = ∑ t ∣ n t μ ( t ) S(n)=\sum_{t\mid n}t\mu(t) S(n)=tntμ(t),这个函数有显然的递推式,设 n n n的唯一分解为 n = ∏ i = 1 t p i k i n=\prod\limits_{i=1}^tp_i^{k_i} n=i=1tpiki,则 S ( n ) = ∏ i = 1 n ( 1 − p i ) S(n)=\prod\limits_{i=1}^n(1-p_i) S(n)=i=1n(1pi)

利用Pollard-Rho筛出所有质因子,dfs找出所有因数和 S S S函数值就行了。


代码(很丑,丑到我都不想看):

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

inline ll mul(cs ll &a,cs ll &b,cs ll &mod){
	return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}

inline ll quickpow(ll a,ll b,cs ll &mod){
	ll res=1;
	while(b){
		if(b&1)res=mul(res,a,mod);
		a=mul(a,a,mod);
		b>>=1;
	}
	return res;
}

inline ll gcd(ll a,ll b){
	static ll tmp;
	while(b){
		tmp=a%b;
		a=b;
		b=tmp;
	}
	return a;
}

cs int P=1e7+7;
int prime[P],pcnt,minpri[P];
bool mark[P];
inline void linear_sieves(int len=P-7){
	mark[1]=true;
	for(int re i=2;i<=len;++i){
		if(!mark[i])prime[++pcnt]=i,minpri[i]=i;
		for(int re j=1;i*prime[j]<=len;++j){
			mark[i*prime[j]]=true;
			minpri[i*prime[j]]=minpri[i];
			if(i%prime[j]==0)break;
		}
	}
}

inline bool isprime(ll x){
	if(x<=P-7)return !mark[x];
	ll t=x-1,s=0;
	while(!(t&1))t>>=1,++s;
	for(int re i=1;i<=5;++i){
    	ll p=prime[rand()%pcnt+1]%x;
        ll num=quickpow(p,t,x),pre=num;
        if(x%p==0)return false;
        for(int re j=0;j<s;++j){
            num=mul(num,num,x);
            if(num==1&&pre!=1&&pre!=x-1)return false;
            pre=num;
        }
        if(num!=1)return false;
    }
    return true;
}

inline ll Pollard_Rho(ll x){
	if(x%2==0)return 2;
	if(x%3==0)return 3;
	if(x%5==0)return 5;
	if(x%7==0)return 7;
	ll n=0,m=0,q=1,t=1,c=rand()%(x-1)+1;
	for(ll re k=2;;k<<=1,m=n,q=1){
		for(ll re i=1;i<=k;++i){
			n=(mul(n,n,x)+c)%x;
			q=mul(q,abs(n-m),x);
		}
		if((t=gcd(q,x))>1)return t;
	}
}

ll pfact[60];
int pfcnt;

inline void sieve(ll x){
	if(x==1)return ;
	if(x<=P-7){
		while(x>1){
			pfact[++pfcnt]=minpri[x];
			int p=minpri[x];
			while(x%p==0)x/=p;
		}
		return ;
	}
	if(isprime(x)){
		pfact[++pfcnt]=x;
		return ;
	}
	ll p=x;
	while(p==x)p=Pollard_Rho(p);
	sieve(p);
	while(x%p==0)x/=p;
	sieve(x);
}

ll n,k,mod;
ll fact[P];
int fcnt;
tr1::unordered_map<ll,ll>f;

inline ll quickpow(ll a,ll b){ll res=1;while(b){if(b&1)res=mul(res,a,mod);a=mul(a,a,mod);b>>=1;}return res;}
inline ll mul(cs ll &a,cs ll &b){return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;}
inline void dfs(ll now,ll nowf,int pos){
	if(pos>pfcnt){
		fact[++fcnt]=now,f[now]=nowf;
		return ;
	}
	dfs(now,nowf,pos+1);
	for(ll p=pfact[pos];;p*=pfact[pos]){
		dfs(now*p,mul(nowf,(mod-pfact[pos]%mod+1)%mod),pos+1);
		if((n/p)%pfact[pos])return ;
	}
}

inline void solve(){
	scanf("%lld%lld%lld",&n,&k,&mod);k%=mod;
	ll ans=0;
	sieve(n);
	sort(pfact+1,pfact+pfcnt+1);
	pfcnt=unique(pfact+1,pfact+pfcnt+1)-pfact-1;
	dfs(1,1,1);
	for(int re i=1;i<=fcnt;++i){
		ll d=fact[i];
		if((d%2)==1&&(n/d)%2==0)continue;
		ans=(ans+mul(f[n/d],mul(quickpow(k,(d+1)/2),(d%2?d:d/2))))%mod;
	}
	cout<<ans<<"\n";
}

inline void init(){
	pfcnt=0;
	fcnt=0;
	f.clear();
}

int T;
signed main(){
	srand(time(0));
	linear_sieves();
	scanf("%d",&T);
	while(T--){
		init();
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值