HDU 2865 Birthday Toy(Polya综合)

题目链接;
HDU 2865 Birthday Toy
题意:
有一个 n 个珠子的环,中心还有一颗珠子,用k种颜色来染。要求相邻珠子的颜色不同,中心珠子的颜色不能和外围任意一颗珠子的颜色一样,考虑旋转,问本质不同的珠子个数?
数据范围: n109,k109
分析:
和前面POJ 2888 Magic Bracket类似,这里把限制改成了相邻珠子颜色不同且颜色个数范围变为了 109 .
对于循环个数 k ,定义dp[i][0]表示一个循环的的珠子经历 i 个循环颜色和循环起始珠子颜色一致的方案数,定义dp[i][1]表示颜色不一样(初始化 dp[1][0]=1,dp[1][1]=0 ),那么可以得到转移方程:

dp[i][0]=dp[i1][1],
dp[i][1]=dp[i1][1](m2)+dp[i][0](m1)

因为 m 很大,用矩阵快速幂进行加速。定义矩阵:

A=(0m11m2)

B=(10)

C=AkB

那么循环个数为 k 总的方案数(考虑起始珠子有m种颜色可选)是: ϕ(nk)mC[1][1] 条件是 n%k=0 ,最后还要乘上 nmod 的逆元。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const ll mod = (ll)(1e9) + 7;

struct Matrix{
    int row, col;

    ll data[10][10];

    Matrix () {}
    Matrix (int k) {
        row = col = 2;
        data[1][1] = 0, data[1][2] = 1;
        data[2][1] = k - 2, data[2][2] = k - 3;
    }

    Matrix operator * (const Matrix& rhs) const {
        Matrix res;
        res.row = row, res.col = rhs.col;
        for(int i = 1; i <= res.row; ++i) {
            for (int j = 1; j <= res.col; ++j) {
                res.data[i][j] = 0;
                for(int k = 1; k <= col; ++k) {
                    res.data[i][j] += data[i][k] * rhs.data[k][j];
                }
                res.data[i][j] %= mod;
            }
        }
        return res;
    }

    Matrix operator ^ (const int m) const {
        Matrix res, tmp;
        tmp.row = res.row = row, tmp.col = res.col = col;
        memset(res.data, 0, sizeof(res.data));
        for(int i = 1; i <= row; ++i) res.data[i][i] = 1;
        memcpy(tmp.data, data, sizeof(data));

        int mm = m;
        while(mm) {
            if(mm & 1) res = res * tmp;
            tmp = tmp * tmp;
            mm >>= 1;
        }
        return res;
    }
};

inline ll phi(ll x)
{
    ll res = x;
    for(ll i = 2; i * i <= x; ++i) {
        if(x % i == 0) {
            res = res / i * (i - 1);
            while(x % i == 0) x /= i;
        }
    }
    if(x > 1) res = res / x * (x - 1);
    return res;
}

ll solve(int len, int k)
{
    Matrix res, tmp;
    tmp = Matrix(k);
    tmp = tmp ^ len;

    res.row = 2, res.col = 1;
    res.data[1][1] = 1, res.data[2][1] = 0;
    res = tmp * res;
    return res.data[1][1];
}

ll quick_pow(int x, ll m)
{
    ll res = 1, tmp = x;
    while(m) {
        if(m & 1) res = res * tmp % mod;
        tmp = tmp * tmp % mod;
        m >>= 1;
    }
    return res;
}

int main()
{
    int n, k;
    while(~scanf("%d%d", &n, &k)) {
        ll ans = 0;
        for(int i = 1; i * i <= n; ++i) {
            if(n % i) continue;
            ans = (ans + phi(n / i) * solve(i, k) % mod) % mod;
            if(n / i == i) continue;
            ans = (ans + phi(i) * solve(n / i, k) % mod) % mod;
        }
        printf("%lld\n",ans * k % mod * (k - 1) % mod * quick_pow(n, mod - 2) % mod);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值