Different Circle Permutation HDU - 5868(burnside引理+递推+矩阵快速幂+欧拉函数)

本文探讨了一个关于学生在圆形花坛周围放风筝排列的问题,并通过应用Polya计数原理和Burnside引理,结合矩阵快速幂算法,提供了一种高效解决此问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You may not know this but it’s a fact that Xinghai Square is Asia’s largest city square. It is located in Dalian and, of course, a landmark of the city. It’s an ideal place for outing any time of the year. And now:

There are NN children from a nearby primary school flying kites with a teacher. When they have a rest at noon, part of them (maybe none) sit around the circle flower beds. The angle between any two of them relative to the center of the circle is always a multiple of 2π/N but always not 2π/N.

Now, the teacher raises a question: How many different ways there are to arrange students sitting around the flower beds according to the rule stated above. To simplify the problem, every student is seen as the same. And to make the answer looks not so great, the teacher adds another specification: two ways are considered the same if they coincide after rotating.
Input
There are T tests (T≤50). Each test contains one integer N. 1N1000000000(109). Process till the end of input.
Output
For each test, output the answer mod 1000000007(109+7) in one line.
Sample Input
4
7
10
Sample Output
3
5
15

这题看了以后就知到考的polya技术,但是是带有限制的,这里带有限制的题,我做的不多,得用burnside引理,具体参见组合数学最后一章。
题目相当于一个染色问题,但是相邻两个不能同时染色,而且旋转同构
先看旋转不同构的情况有多少种,设为g(n),其实正规解法是要去递推来求,但是推导能力得很强,找规律也不是不可以的,我们可以发现g(n)=g(n1)+g(n2)g(1)=1g(2)=3
我们递推来求试试:设f(n)为染色结尾的所有可能,设h(n)为不染色结尾的所有可能(且全部为链状而不是环)g(n)为环状时的肯能数,那么有:

h(n)=f(n1)+h(n1)
f(n)=h(n1)

显然可以吧f(n)替代有:
h(n)=h(n2)+h(n1)
,现在把它连成环,自然也是合法的,现在考虑f(n),这个是不能直接连成环的,因为必须考虑它的头有没有染色,那么我们只要从第2个开始考虑,第一个就默认为没有染色了,所有有:
g(n)=h(n)+f(n1)
f(n1)替代有:
g(n)=h(n)+h(n2)

因为:
g(n1)+g(n2)=h(n1)+f(n3)+h(n2)+f(n4)=h(n)+h(n2)=g(n)

所以g(n)也是斐波那契数列

最后根据burnside引理有:

ans=1ni=1ng(gcd(i,n))

但是线性枚举依然超时的,所以经典套路去枚举gcd,引入欧拉函数ϕ
ans=1nd|nng(d)ϕ(nd)

至此理论部分完成(默认你理解相关群论知识)

#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 1000000007
using namespace std;
typedef long long ll;
int phi(int x)//欧拉函数
{
    int  ans=x;
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            ans-=ans/i;
            while(x%i==0)
                x/=i;
        }
        if(x==1)
        break;
    }
    if(x!=1)
        ans-=ans/x;
    return ans;
}
struct M//矩阵求斐波那契数列
{
    ll a[2][2];
    void init()
    {
        memset(a,0,sizeof(a));
    }
    void unit()
    {
        init();
        a[0][0]=1;
        a[1][1]=1;
    }
    M muil(M x)
    {
        M ans;
        for(int i=0;i<2;i++)
        {
            for(int j=0;j<2;j++)
            {
                ans.a[i][j]=0;
                ans.a[i][j]=(a[i][0]*x.a[0][j]%mod+a[i][1]*x.a[1][j])%mod;
            }
        }
        return ans;
    }
};
ll f(int x)//矩阵快速幂
{
    if(x==1)
    return 1;
    if(x==2)
    return 3;
    x-=2;
    M ans;
    ans.unit();
    M u;
    u.a[0][0]=1;
    u.a[0][1]=1;
    u.a[1][0]=1;
    u.a[1][1]=0;
    while(x)
    {
        if(x&1)
            ans=ans.muil(u);
        u=u.muil(u);
        x>>=1;
    }
    return (ans.a[0][0]*3+ans.a[0][1])%mod;
}
ll P(ll a,ll b)
{
    ll ans=1;
    ll temp=a;
    while(b)
    {
        if(b&1)
            ans=ans*temp%mod;
        temp=temp*temp%mod;
        b>>=1;
    }
    return ans;
}
ll getInv(int n)//求逆元,因为有除法取模
{
    return P(n,mod-2);
}
ll solve(int n)
{
    ll ans=0;
    int L;
    for(L=1;L*L<n;L++)
    {
        if(n%L==0)
        {
            ans=(ans+phi(n/L)*f(L))%mod;
            ans=(ans+phi(L)*f(n/L))%mod;//折半枚举
        }
    }
    if(L*L==n)
        ans=(ans+phi(L)*f(L))%mod;  
    ans=ans*getInv(n)%mod;
    return ans;
}
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        if(n==1)//1的时候特判一下
            printf("2\n");
        else
            printf("%lld\n",solve(n));
    }
    return 0;
}
### 使用 `next_permutation` 处理数组前 5 个元素 在 C++ 中,STL 提供了一个非常有用的函数 `std::next_permutation`,它能够生成下一个排列组合。此函数通常用于对整个容器进行全排列操作,但如果只想针对部分数据(如数组的前五个元素),可以通过传递适当的迭代器范围实现。 以下是具体实现方法: #### 实现代码 ```cpp #include <iostream> #include <algorithm> using namespace std; int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7}; // 原始数组 int n = sizeof(arr) / sizeof(arr[0]); // 获取数组大小 // 输出初始状态下的前 5 个元素 cout << "Initial permutation: "; for(int i = 0; i < 5; ++i){ cout << arr[i] << ' '; } cout << endl; do{ // 打印当前排列 for(int i = 0; i < 5; ++i){ cout << arr[i] << ' '; } cout << endl; } while(next_permutation(arr, arr + 5)); // 对前 5 个元素应用 next_permutation return 0; } ``` 上述代码展示了如何仅对数组的前五个元素执行所有可能的排列组合,并打印每种排列的结果。注意,在每次循环中,只有数组的第一个至第五个位置被重新排列,而其余部分保持不变[^1]。 #### 关键点解析 - **`next_permutation` 的参数**: 此函数接受两个随机访问迭代器作为其主要参数,分别指向要处理区间的起始和结束位置。因此可以精确控制哪些元素参与排列。 - **终止条件**: 当无法再找到更大的字典序排列时,`next_permutation` 返回 false,从而退出循环[^2]。 通过这种方式,即使对于较大的数组或者向量结构体来说,也可以灵活地选取其中一部分来进行复杂的排列运算而不影响整体数据完整性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值