5330: [Sdoi2018]反回文串【莫比乌斯反演+Miller-Robin】

Description

“回文串什么的最讨厌了……”
小Q讨厌任何形式的回文串:
(1)如果一个字符串从左往右读和从右往左读是一样的,那么小Q讨厌它;例如aa和aba
(2)对于一个字符串来说,若将某个前缀子串移除并拼接到字符串的尾部,能得到一个小Q讨厌的字符串,
那么小Q也会讨厌原来的这个字符串;例如aab和baa。
现在问题来了,如果任意字符串只可以由k种已知的字符组成(也就是说字符集的大小为k),
那么长度为n的所有字符串里,有多少字符串是小Q讨厌的?
答案可能很大,你只需要给出答案对p取模后的值。

Input

第一行包含一个正整数T,表示有T组测试数据。
接下来T行,每行描述一组测试数据,包含三个正整数n,k和p。
1<=T<=10,1<=n<=10^18,1<=k<=n,10^9<=p<=2^30

Output

对于每组测试数据,输出一行,包含一个整数,表示答案对p取模的值。

Sample Input

10

9311702400 2063322107 1032035101

9311702400 3847844404 1014424217

9311702400 6992683534 1067435323

9311702400 1356819643 1024817347

9311702400 6944668829 1042812553

9311702400 9162670852 1003177793

9311702400 6567188538 1062813337

9311702400 3910301217 1018910293

9311702400 2463242636 1018694167

9311702400 4797739673 1050709027

Sample Output

618961590

28597316

1016219343

977847638

108994801

100559666

694084378

492868358

336126742

176095716

解题思路:

原来SDOI2018的莫比乌斯反演在这里……

考虑一个长度为 n 回文串,将其循环移位后所有的串都是满足要求的串。

但是显然这样计算会算重。考虑什么情况下会算重。

即当我们将这个回文串移位x后,发现这个新字符串为一个回文串时,必然接下来的移位都是重复的。

那么当x为多少时,新字符串为一个回文串?

我们稍加分析就会发现x一定和回文串的最小循环节 d 有关。

考虑最小循环节若为偶数时,当 x == d / 2 时,则会变为一个新的回文串。

否则, x == d 时,会出现一个新的回文串。

那么我们设 f ( d ) 表示长度为n,字符集为k,最小循环节为d的字符串的数量。

显然会有 d | n f ( d ) == k n / 2

g ( n ) = k n / 2

由莫比乌斯反演则有 f ( d ) = n | d μ ( n / d ) g ( n )

那么 A n s = d | n f ( d ) d 1 + [ d ]

我们设 H ( d ) = d 1 + [ d ] ,那么:

A n s = d | n f ( d ) H ( d )
= d | n H ( d ) m | d μ ( d m ) g ( d )
= m | n g ( m ) d | n m μ ( d ) H ( d m )

注意只有当 m 为奇数, n m 为偶数时 d | n m μ ( d ) H ( d m ) = 0 ,因为此时 μ ( x ) H ( x m ) μ ( 2 x ) H ( 2 x m ) (x为奇数)一一对应,所以和为0。

其余情况下很容易证明 H ( d m ) = H ( m ) d ,所以:

A n s = m | n g ( m ) H ( m ) d | n m μ ( d ) d

d | n μ ( d ) d 正是 p | n ( 1 p i ) p i n 的质因数分解) 的展开式,所以:

A n s = m | n g ( m ) H ( m ) p | n m ( 1 p i )

用Miller-Robin算法将n分解质因数后枚举因数即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll getint()
{
    ll i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
ll n,k,mod,m,ans,yz[100];
map<ll,int>mp;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll mul(ll x,ll y,ll p)
{
    ll res=x*y-(ll)((long double)x*y/p+0.5)*p;
    return res<0?res+p:res;
}
ll Pow(ll x,ll y,ll p)
{
    ll res=1;x%=p;
    for(;y;y>>=1,x=mul(x,x,p))
        if(y&1)res=mul(res,x,p);
    return res;
}
bool check(ll x,ll n,ll r,ll s)
{
    x=Pow(x,r,n);ll pre=x;
    while(s--)
    {
        x=mul(x,x,n);
        if(x==1&&pre!=1&&pre!=n-1)return false;
        pre=x;
    }
    return x==1;
}
bool MR(ll n)
{
    if(n==1)return false;
    if(n==2)return true;
    if(!(n&1))return false;
    ll r=n-1,s=0;
    while(!(r&1))r>>=1,s++;
    for(int i=0;i<9;i++)if(!check(rand()%(n-1)+1,n,r,s))return false;
    return true;
}
ll pho(ll n,ll c)
{
    ll x=rand()%n,y=x,k=2,p=1;
    for(int i=1;p==1;i++)
    {
        x=(mul(x,x,n)+c)%n;
        p=gcd(n,abs(x-y));
        if(i==k)y=x,k<<=1;
    }
    return p;
}
void solve(ll n)
{
    if(n==1)return;
    if(MR(n))
    {
        if(!mp[n])yz[++m]=n;
        mp[n]++;return;
    }
    ll t=n;while(t==n)t=pho(n,rand()%(n-1)+1);
    solve(t),solve(n/t);
}
void dfs(int i,ll d,ll s)
{
    if(i==m+1)
    {
        if((d&1)&&(!(n/d&1)))return;
        ans=(ans+mul(Pow(k,(d+1)/2,mod),((d&1)?d:d/2),mod)*(s%mod+mod)%mod)%mod;
        return;
    }
    int cnt=mp[yz[i]];ll t=1;
    while(cnt--)dfs(i+1,d*t,s*(1-yz[i])),t*=yz[i];
    dfs(i+1,d*t,s);
}
void solve()
{
    n=getint(),k=getint(),mod=getint();
    m=0,mp.clear(),solve(n);
    ans=0,dfs(1,1,1);
    printf("%lld\n",ans);
}
int main()
{
    //freopen("cycpal.in","r",stdin);
    for(int T=getint();T;T--)solve();
}

猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/80455655