HDU 2481 Toy(Polya综合)

题目链接:
HDU 2481 Toy
题意:
有外围 n 个点围着中心一个点,中心有n条边和外围相连,外围的相邻点有一条边,现在需要从中去除 n 条边,使得剩下的n+1个点依然保持两两连通,考虑旋转,问有多少种方案?方案数对 M 取模。
数据范围:3n109,2M109.
分析:
我觉得这种考验智商的题目真的不适合窝。。。
主要参考博客:
cx_love
将狼踩尽
zxb

任意取两个结点讨论 a,b 。那么总数便是 a,b 断开的种数与 a,b 连在一起的种数的和。

f(n)na,b

g(n)na,b

  • 如果 a,b 之间是断开的,假设与 a 直接相连的为k个(加上 a 自己),那么显然这k个要与其它的保持连通的,与中心必须有一条边,如果有多条边就形成环了,显然不满足生成树。另外 nk 个外围的其余点,自成一圈,所以有 f[nk] 种。我们可以枚举 k ,则

    f[n]=i=0i<n(if[ni])

    初始化: f[0]=f[1]=1,f[2]=3

  • 如果 a,b 是连在一起的,如果与 a,b 相连的为 k 个(包括a,b),那么 a,b 是相邻的在这 k 个位置选择就有k1种,而这 k 个与中心相连的选择有k种,剩下的与这部分是分开的,也是自成一圈,则为 f[nk] ,所以可以枚举 k ,最终结果:

    g[n]=i=2i<n(i(i1)f[ni])

    初始化: g[1]=0

则最终的种数便是

T[n]=f[n]+g[n]

前方高能
我们来化简: f[n]g[n]
f[n]=i=0i<n(if(ni))

f[n]=f(n1)+2f(n2)+3f(n3)(n1)f(1)+nf[0]=f(n1)+f(n2)+f(n3)f(1)+f(0)+(f(n2)+2f(n3)+(n1)f(0))

s[n] f[i] 的前 n 项和,则上式可以写成:

f[n]=s[n1]+f(n2)+2f(n3)(n2)f(1)=s[n1]+i<n1i=0(if((n1)i))=s[n1]+f[n1](1)s[n1]=f[n]f[n1]=s[n2]+f[n1]+f[n1]=s[n2]+2f[n1]=f[n1]f[n2]+2f[n1](1)s[n2]=3f[n1]f[n2]f[0]=1,f[1]=1,f[2]=3,f[3]=8

g(n)=i<ni=1[i(i1)f(ni)]
g(n)=12f[n2]+23f[n3]+34f[n4](n1)(n2)f[1]
g(n1)=12f[n3]+23f[n4]+(n2)(n3)f[1]
g(n)g(n1)=2f[n2]+4f[n3](2(n2))f[1]=2(f[n2]+2f[n3]+f[1])=2f[n1]

这个是最基本的递推式了。。
g[n]=2(f[1]+f[2]+f[3]f[n1])=2(s[n1]f[0])=2(f[n]f[n1]1)f[0]=1

(0113)

右乘
(f(n2)f(n1))

得到

(f(n1)f(n))

初始化:
(13)

对于 f[n] 的求法,可以用矩阵快速幂乘解决.
g[n] 也就可以顺便得到, T[n] 就处理完毕了。
然后就是 Burnside ,同样 N 比较大,肯定是要用欧拉函数优化,枚举循环个数。
nMOD是极有可能没有逆元的。
只能用 (a/b) 。这样的话把取模就变成了 MODN ,范围一下子到了 1018 ,这样子的话中间的乘法便会溢出 64 位整数。
所有的大整数相乘都得二分模拟。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <climits>
#include <cmath>
#include <ctime>
#include <cassert>
#include <bitset>
#define IOS ios_base::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
const int MAX_N = 100010;

int prime[MAX_N], prime_cnt;
bitset<MAX_N> bs;


void GetPrime()
{
    prime_cnt = 0;
    bs.set();
    for(int i = 2; i < MAX_N; ++i) {
        if(bs[i]) prime[prime_cnt++] = i;
        for(int j = 0; j < prime[i] && i * prime[j] < MAX_N; ++j) {
            bs[i * prime[j]] = 0;
            if(i % prime[j] == 0) break;
        }
    }
}

struct Matrix {
    int row, col;
    ll data[10][10];
};

inline ll mul_mod(ll a, ll b, ll mod)
{
    a = (a % mod + mod) % mod;
    b = (b % mod + mod) % mod;
    ll res = 0, tmp = a;
    while(b > 0) {
        if(b & 1) {
            res += tmp;
            if(res > mod) res -= mod;
        }
        tmp <<= 1;
        if(tmp > mod) tmp -= mod;
        b >>= 1;
    }
    return res;
}

Matrix mat_mul_mod(Matrix a, Matrix b, ll mod) {
    Matrix res;
    res.row = a.row, res.col = b.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 <= b.row; ++k) {
                res.data[i][j] += mul_mod(a.data[i][k], b.data[k][j], mod) % mod;
            }
            res.data[i][j] %= mod;
        }
    }
    return res;
}

Matrix mat_quick_pow(Matrix a, ll m, ll mod) 
{
    Matrix res, tmp;
    res.row = tmp.row = a.row, res.col = tmp.col = a.col;
    memset(res.data, 0, sizeof(res.data));
    for(int i = 1; i <= res.row; ++i) { res.data[i][i] = 1; }
    memcpy(tmp.data, a.data, sizeof(tmp.data));

    while(m) {
        if(m & 1) res = mat_mul_mod(res, tmp, mod);
        tmp = mat_mul_mod(tmp, tmp, mod);
        m >>= 1;
    }
    return res;
}

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

ll solve(ll k, ll mod)
{
    if(k == 1) return 1;
    Matrix res, tmp;
    tmp.row = tmp.col = 2;
    tmp.data[1][1] = 0, tmp.data[1][2] = 1;
    tmp.data[2][1] = -1, tmp.data[2][2] = 3;
    tmp = mat_quick_pow(tmp, k - 2, mod);

    res.row = 2, res.col = 1;
    res.data[1][1] = 1, res.data[2][1] = 3;
    res = mat_mul_mod(tmp, res, mod);

    ll x = res.data[1][1], y = res.data[2][1];
    ll ans = mul_mod(y - x - 1, 2, mod);
    return ((ans + y) % mod) % mod;
}

ll Polya(ll n, ll m) 
{
    ll res = 0, mod = n * m;
    for(ll i = 1; i * i <= n; ++i) {
        if(n % i) continue;
        res = (res + mul_mod(phi(n / i), solve(i, mod), mod)) % mod;
        if(n / i == i) break;
        res = (res + mul_mod(phi(i), solve(n / i, mod), mod)) % mod;
    }
    return res / n;
}

int main()
{
    GetPrime();
    ll n, m;
    while(~scanf("%lld%lld", &n, &m)) {
        printf("%lld\n", Polya(n, m));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值