[burnside引理] HDU5868 Different Circle Permutation

Different Circle Permutation

题意:

n个球围成一个圈,每个球是白色或黑色,要求不能有连续的黑球相邻,并且旋转同构,求有多少种合法方案。

思路:

设不考虑旋转同构时, n 个球的方案为f(n),手算下 f(2)=3,f(3)=4,f(4)=7f(5)=11 ,好像有点 f(n)=f(n1)+f(n2) 的意思,然后从转移的角度思考下,假设n-1个球已经放好,第n个球放在最后,如果是白求,这就是 f(n1) ,如果是黑球,那么第n-2个和第1个都不能是黑球,这就是 f(n2) ,也符合之前观察的式子 f(n)=f(n1)+f(n2) 。 逆推得到 f(1)=1 ,但是实际上 f(1)=2 ,个人理解是不能是黑色,因为此时自己和自己算相邻,然而题目认为1时答案为2,这个特判下就行,然后观察形式f可以矩阵快速幂,这就是不考虑同构的情况

同构时需要使用burnside引理。
考虑循环位移,每个元素可以顺时针移 1,2,3,...n 个元素设为,设 F(k) n 个元素位移k的置换, C(F(k))F(k) ,根据burnside引理,答案就是等价类的数目= 1Nnk=1C(F(k))
然后推导下不动点,考虑 F(i) ,就是一个元素不断位移i次,最后回到i,路上经过的元素就是一个置换群,经过的点数其实就是 n/gcd(i,n) ,那么一共有 n/(n/gcd(i,n)) 个,即 gcd(i,n) 个不同的置换群,那么 C(F(i))=f(gcd(i,n))

answer=1nk=1nf(gcd(k,n))=1nj=1nf(j)k=1n[gcd(k,n)==j]=1nj|nf(j)k=1n[gcd(k/j,n/j)==1]=1nj|nf(j)k=1n/j[gcd(k,n/j)==1]=1nj|nf(j)π(n/j)

枚举n的约数即可算答案。

//
//  main.cpp
//  5868
//
//  Created by 翅膀 on 16/9/15.
//  Copyright © 2016年 kg20006. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
ll euler(ll n) {
    ll res = n, a = n;
    for(ll i = 2; i*i <= a; i++){
        if(a%i == 0){
            res = res/i*(i-1);
            while(a%i == 0) a /= i;
        }
    }
    if(a > 1) res = res/a*(a-1);
    return res;
}
struct mat{
    ll r[2][2];
    mat(){ memset(r, 0, sizeof(r)); }
    mat operator *(const mat& z) {
        mat res;
        for(int i = 0; i < 2; ++i) {
            for(int j = 0; j < 2; ++j) {
                for(int k = 0; k < 2; ++k) {
                    res.r[i][j] += r[i][k]*z.r[k][j];
                    res.r[i][j] %= mod;
                }
            }
        }
        return res;
    }
}a, b;
ll f(ll k) {
    if(k == 1) return 1;
    else if(k == 2) return 3;
    else if(k == 3) return 4;
    k -= 3;
    mat res, aa = b;
    res.r[0][0] = res.r[1][1] = 1;
    while(k) {
        if(k&1) res = res*aa;
        aa = aa*aa;
        k >>= 1;
    }
    res = res*a;
    return res.r[0][1];
}
ll qpow(ll a, ll k) {
    ll res = 1;
    while(k) {
        if(k&1) res = (res*a)%mod;
        a = (a*a)%mod;
        k >>= 1;
    }
    return res;
}
int main(int argc, const char * argv[]) {
    a.r[0][0] = 3, a.r[0][1] = 4, a.r[1][0] = 1, a.r[1][1] = 3;
    b.r[0][0] = b.r[0][1] = b.r[1][0] = 1;
    ll n;
    while(scanf("%lld", &n) != EOF) {
        if(n == 1) { printf("2\n"); continue; }
        ll ans = 0;
        for(ll k = 1; k*k <= n; ++k) {
            if(n%k == 0) {
                ans += f(k)*euler(n/k)%mod;
                if(k*k != n) ans += f(n/k)*euler(k)%mod;
                ans %= mod;
            }
        }
        ans = ans*qpow(n, mod-2)%mod;
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值