题解 HDU 2865 Birthday Toy

传送门


【大意】

给定一个玩具,其周围有 (n) 个小珠子,中间有 (1) 个大珠子。要求用 (k) 个颜色对所有珠子着色,要求任意相邻小珠子颜色互不相同,大珠子与任意小珠子颜色互不相同。问旋转同构的方案数。


【分析】

优先枚举中心大珠子的着色方案,为 (k) 种。等价于用 ((k-1)) 个颜色对剩余 (n) 个小珠子着色的方案数。

显然,绕圆心均匀旋转 (0)~((n-1)) 个小珠子的操作 ({ rot_0, rot_1, rot_2, cdots, rot_{n-1} }) 构成一个置换群 (G)

考虑使用 Burniside 引理求解: (displaystyle kcdot{1over |G|}sum_{gin G}|X^g|={kover n}sum_{i=0}^{n-1}f(i, k-1))

其中,(f(i, k-1)) 表示所有相邻颜色不同的着色方案中,绕圆心旋转 (i) 个小珠子的操作 (rot_i) 不动点数

不难想到,所有着色方案中,若是 (rot_i) 的不动点,则对于任意序号 (a) 的小珠子,它的颜色必须和序号为 ((a+i)) 的小珠子相同

因此,(rot_i) 操作将小珠子的序号划分为 (gcd(i, n)) 个等价类,而同一等价类的染色方案必须相同

(gcd(i, n)=1) 时,由于相邻小珠子染色不同,方案数为 (0)

(gcd(i, n)=d) 时,用 (h(d, k-1)) 表示其染色方案数

代入原式得到:(displaystyle {kover n}sum_{i=0}^{n-1}h(gcd(i, n), k-1)={kover n}sum_{i=1}^n h(gcd(i, n), k-1))

采用莫比乌斯反演:(displaystyle {kover n}sum_{i=1}^n h(gcd(i, n), k-1)={kover n}sum_{dmid n\d>1}h(d, k-1)sum_{i=1}^n [gcd(i, n)=d]={kover n}sum_{dmid n\d>1}h(d, k-1)cdot oldsymbol varphi({nover d}))


现考虑如何计算 (h(d, k),d>1) ,表示 (n) 被划分为 (d) 个等价类,用 (k) 个颜色进行着色,使得相邻等价类颜色不同的旋转同构方案数

考虑将 (d) 个等价类看成 (d) 元环上的不同点,则 (h(d, k)) 等价于求解用 (k) 个颜色对 (d) 元环进行着色,使得相邻点颜色不同的旋转不同构方案数

(h(2, k)=k(k-1), h(3, k)=k(k-1)(k-2))

(d>3) 时,考虑破环成链,考虑链上第一个点和倒数第二个点颜色是否相同:

  1. 若相同,则最后一个点的着色方案数为 ((k-1)) 。前 ((d-1)) 个点由于第一个和倒数第二个颜色相同(等价于一个点),着色方案为 (h(d-2, k)) 。由乘法原理,方案数为 ((k-1)cdot h(d-2, k))

  2. 若不同,则最后一个点的着色方案数为 ((k-2)) 。前 ((d-1)) 个点由于第一个和倒数第二个颜色不同,可以直接相连,故着色方案数为 (h(d-1, k)) 。由乘法原理,方案数为 ((k-2)cdot h(d-1, k))

故根据加法原理,(h(d, k)=(k-1)cdot h(d-2, k)+(k-2)cdot h(d-1, k))

特征方程为 (x^2+(1-k)x+(2-k)=[x-(k-1)]cdot [x-(-1)]=0) ,得 (h(d, k)=C_1cdot (k-1)^d+C_2cdot (-1)^d)

代入 (h(2, k))(h(3, k))(h(d, k)=(k-1)^d+(k-1)cdot (-1)^d)


由于 (oldsymbol varphi({nover d})) 均为 (n) 的因数,因此本人通过 divit 预处理 (n) 的所有质因数,优化求解复杂度

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=1e9+7;
inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%MOD) if(x&1) ans=ans*a%MOD; return ans; }
inline int add(int a, int b) { return (a+=b)>=MOD?a-MOD:a; }
inline int dis(int a, int b) { return (a-=b)<0?a+MOD:a; }

const int Lim=4e4, MAXN=Lim+10;
int fc[MAXN], prime[MAXN], cntprime;
inline void sieve(){
    for(int i=2;i<=Lim;++i){
        if(!fc[i]) fc[i]=prime[++cntprime]=i;
        for(int j=1;j<=cntprime;++j)
            if(prime[j]>fc[i]||prime[j]*i>Lim) break;
            else fc[ prime[j]*i ]=prime[j];
    }
}

int n, k;
vector<int> divi;
inline void divit(int n){
    divi.clear();
    for(int i=2, I=sqrt(n)+1e-6;i<=I;++i) if(n%i==0) {
        while(n%i==0) n/=i;
        divi.push_back(i);
    }
    if(n>1)
        divi.push_back(n);
}
inline int phi(int n){
    int res=n;
    for(auto p : divi)
        if(n%p==0)
            res=res/p*(p-1);
    return res;
}
inline int f(int n, int k) {
    return n&1?dis( fpow(k-1, n) , k-1):add( fpow(k-1, n) , k-1);
}
inline int ans(int n, int k){
    int res=f(n, k-1);
    divit(n);
    for(int i=2, j, I=sqrt(n)+1e-6;i<=I;++i) if(n%i==0) {
        j=n/i;
        res=add(res, (ll)f(i, k-1)*phi(j)%MOD );
        if(i!=j) res=add(res, (ll)f(j, k-1)*phi(i)%MOD );
    }
    return fpow(n, MOD-2)*k%MOD*res%MOD;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    sieve();
    while(cin>>n>>k)
        cout<<ans(n, k)<<"
";
    cout.flush();
    return 0;
}
原文地址:https://www.cnblogs.com/JustinRochester/p/15058888.html