Different Circle Permutation HDU – 5868题解

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πN2πNbut always not 2πN2π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 TT tests (T≤50T≤50). Each test contains one integer NN1≤N≤1000000000 (109)1≤N≤1000000000 (109). Process till the end of input.

Output

For each test, output the answer mod 1000000007 (109+7109+7) in one line.

Sample Input

4

7

10

Sample Output

3

5

15

 

中文题目大意:n个小朋友个一个老师去一个城市广场放风筝,当n个小朋友停下来休息的时候老师要求他们围着一个圆形的花坛坐下。不一定每个小朋友都坐下休息,可以都不坐下,但是坐下的两个小朋友和花坛圆心组成的角必须是2π/n的倍数,但不能等于2π/n。老师想知道,一共有多少种坐下的方法。为了简化问题,每个小朋友看作是一样的。为了不让答案看起来太大,可以通过旋转变得一样的方法看作是同一种方法。

题目的数学意义:给定一个圆,和整数n,设圆2πrad平均分成n份后一份的度数为k。n表示欲在圆周上插入小于等于n、大于等于0个点,每两个点之间的度数是 k的倍数,但不能是k。问:不同的排列种类数。注:每个点都是一样的,并且是排成一个首尾相接的圆而不是一列或一行,并且满足旋转同构。

输入:

输入挺大的,考虑用longlong。

输出:

每一组数据对1e9+7取模,又是排列组合题(涉及乘法除法),考虑用逆元。

样例输入输出:

                   输入     输出

第一组:4        3

第二组:7        5

 

 

 

第二组:10       15

解题思路:

首先明确目标,求旋转同构的环的排列组合种数。

首先想象一下一根围成圆周的绳子上有n个均匀分布的点,每两点间的圆心角弧度是2π/n。每一种情况就是考虑每个点坐不坐人的情况。从任意两个点中将围成圆的绳子剪开,这时,规定一下绳子的首尾,绳子便有了第一个点和最后一个点。

从第一个点开始dp考虑每个点取0(不坐人)还是取1(坐人)。因为最后求的是种数,所以令dp[i][0]表示第i个座位如果不坐人计算到i为止,有多少种方法;dp[i][1]表示第i个座位如果坐人,计算到i为止,有多少种方法。显然dp[1][0]=1,dp[1][1]=1,表示第一个座位坐人,计算到第一个座位为止,有一种方法;同理,第一个座位不坐人也是一种方法。考虑题目条件,第二个点开始,能不能坐人由前一个点决定(前后两个点是属于相互影响状态,所以采用固定前一个点,后一个点作出让步的办法,当然也能从后往前算)。

第二个点开始,任意一个点i,如果i-1坐人了,i就不能坐人,如果i-1没有坐人则i可以坐人或者不坐人。也就是,第i个座位不坐人,对前一个座位没要求;第i个座位坐人,前一个座位绝对不能坐人。也就是,前一个座位坐人不坐人的方法数都加起来等于后一个座位不坐人的方法数;前一个座位不坐人的方法书等于后一个座位坐人的方法数。则dp[i][0]=dp[i-1][0]+ dp[i-1][1];   dp[i][1]=dp[i-1][0];

i      1       2       3       4       5       6       7       8       9       10     11     12 

dp[i][0]  1        2       3       5       8       13     21     34     55     89     144  233

dp[i][1]     1       1       2       3       5       8       13     21     34     55     89     144

发现是斐波那契数列。

dp到第n个,这个时候考虑把绳子重新首尾相接。显然,接起来后第一个点和最后一个点会相互影响,但是在刚才的dp中并没有考虑到。当最后一个点不坐人的时候,算出来有多少种方法就有多少种方法,当最后一个点坐人的时候,第一个点是确定的,一定不能坐人,所以不用考虑,直接从第二个点开始dp,其实还是斐波那契数列,只是算到第n个的时候实际上是第n-1项。所以,连成环后的方法数是num[n]=dp[n-1][1](最后一个点坐人)+dp[n][0](最后一个点不坐人),num[n]=dp[n-2][0]+dp[n][0],结合斐波那契数列,num[n]=fib[n-2+1]+fib[n+1]=fib[n-1]+fib[n+1];

根据fib[n]=bib[n-1]+fib[n-2];

得num[n]=num[n-1]+num[n-2];

通过num[n]=fib[n-1]+fib[n+1];

计算出num[2]=1+2=3;  num[3]=1+3=4

通过num[n]=num[n-1]+num[n-2];  代入  num[2]=3;  num[3]=4

得 num[1]=1

递推式可以考虑用矩阵快速幂计算。

矩阵快速幂和正常递推效率比较如下:

接下来的推导涉及离散数学。

现在考虑重构问题。

伯恩赛德引理(Burnside's lemma),设G={a1,a2,…ag}是目标集[1,n]上的置换群。每个置换都写成不相交循环的乘积。  是在置换ak的作用下不动点的个数,也就是长度为1的循环的个数。通过上述置换的变换操作后可以相等的元素属于同一个等价类。若G将[1,n]划分成l个等价类,则:

等价类个数为:

https://gss0.bdstatic.com/-4o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D249/sign=4819311e96eef01f49141fc1d9ff99e0/94cad1c8a786c91783a8da43cb3d70cf3bc75725.jpg

Poly定理,设https://gss3.bdstatic.com/-Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D17/sign=f360fc5ca6efce1bee2bcccdae513d5d/d043ad4bd11373f0e18dd035a50f4bfbfaed04ed.jpg是n个对象的一个置换群, 用m种颜色染图这n个对象,则不同的染色方案数为:

https://gss1.bdstatic.com/-vo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D248/sign=d4a125768b13632711edc537a98ea056/d62a6059252dd42aed4ab801023b5bb5c8eab8dd.jpg

其中https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D122/sign=ed7d7e85bf096b6385195a523e328733/0df431adcbef7609ac0d20352fdda3cc7cd99e12.jpghttps://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D33/sign=cb4896d6500fd9f9a417536a242dfc78/6d81800a19d8bc3e9185bf60838ba61ea9d3458e.jpghttps://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D19/sign=ac5213a7f9dcd100c99cfc28738b6765/0b7b02087bf40ad1f54c7e49562c11dfa8eccef4.jpg的循环节数

 

回到题目,考虑旋转同构,按照一样的方法(旋转角度)一次或多次旋转后相互一致的是一类(在题目中被看作一种),这一类里面的每一种情况都有一样长度(和旋转角度有关)的循环节,并且因为旋转后可以重合所以围坐着的人数相同,总座位数也相同,所以循环节个数相同。但是拥有循环节数量、长度一样的环,不一定是一类环,从离散的角度很容易理解。通俗地讲,从“宏观上看”,把每个循环节设成A,这一类里的所有环排列就都是一样的,从微观上看,循环节内部可以不同,而循环节内部的不同造就相同循环节长度下不同的种类(题目中所说的种类)。而循环节所有可能的长度是n的所有因子,也就是对所有i而言的gcd(i ,n),而num(循环节长度)就是该长度下有多少种可能的循环节。这里的num()和另一位博主的g()是一样的,第二个等号的意思如下:

设gcd(i,n)= d

则gcd(i/d,n/d)= 1

有d 个位置的旋转同构环坐人种类数g(d) 的和,

根据乘法分配律,写成k*g(di) 的和,k为整个求和式子中g(di)的个数,

其中k为欧拉函数。

https://i-blog.csdnimg.cn/blog_migrate/bf5ce027ce018aa140b20311485babac.png

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 1000000007ll 
typedef long long ll;
struct Mat
{
    ll mat[3][3];
    int row,col;
};
Mat mod_mul(Mat a,Mat b,int p)
{
    Mat ans;
    ans.row=a.row;
    ans.col=b.col;
    memset(ans.mat,0,sizeof(ans.mat));
    for(int i=0;i<ans.row;i++)      
        for(int k=0;k<a.col;k++)
            if(a.mat[i][k])
                for(int j=0;j<ans.col;j++)
                {
                    ans.mat[i][j]+=a.mat[i][k]*b.mat[k][j]%p;
                    ans.mat[i][j]%=p;
                }
    return ans;
}
Mat mod_pow(Mat a,int k,int p)
{
    Mat ans;
    ans.row=a.row;
    ans.col=a.col;
    for(int i=0;i<a.row;i++)
        for(int j=0;j<a.col;j++)
            ans.mat[i][j]=(i==j);
    while(k)
    {
        if(k&1)ans=mod_mul(ans,a,p);
        a=mod_mul(a,a,p);
        k>>=1;
    }
    return ans;
}
#define maxn 55555
int euler[maxn],prime[maxn],res;
void Get_euler(int n)
{
    memset(euler,0,sizeof(euler));
    euler[1]=1;
    res=0;
    for(int i=2;i<=n;i++)
    {
        if(!euler[i])euler[i]=i-1,prime[res++]=i;
        for(int j=0;j<res&&prime[j]*i<=n;j++)
        {
            if(i%prime[j]) euler[prime[j]*i]=euler[i]*(prime[j]-1);
            else
            {
                euler[prime[j]*i]=euler[i]*prime[j];
                break;
            }
        }
    }
}
ll f[maxn];
void init()
{
    Get_euler(50000);
    f[1]=1,f[2]=3;
    for(int i=3;i<=50000;i++)f[i]=(f[i-1]+f[i-2])%mod;
}
ll get_f(int n)
{
    if(n<=50000)return f[n];
    Mat A;
    A.row=A.col=2;
    A.mat[0][0]=1,A.mat[0][1]=1,
    A.mat[1][0]=1,A.mat[1][1]=0;
    A=mod_pow(A,n-2,mod);
    return 3ll*A.mat[0][0]+A.mat[0][1];
}
ll get_euler(int n)
{
    if(n<=50000)return euler[n];
    ll ans=n;
    for(int i=2;i*i<=n;i++)
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)n/=i;
        }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}
ll Mod_pow(ll a,ll b)
{
    a%=mod;
    ll ans=1ll;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    init();
    int n;
    while(~scanf("%d",&n))
    {
        if(n==1)
        {
            printf("2\n");
            continue;
        }
        ll ans=0;
        for(int i=1;i*i<=n;i++)
            if(n%i==0)
            {
                ll temp=(get_f(i))%mod*get_euler(n/i)%mod;
                ans=(ans+temp)%mod;
                if(i*i!=n)
                {
                    int j=n/i;
                    temp=get_f(j)*get_euler(n/j)%mod;
                    ans=(ans+temp)%mod;
                }           
            }
        printf("%I64d\n",ans*Mod_pow(n,mod-2)%mod);
    }
    return 0;
}

引用:https://blog.csdn.net/v5zsq/article/details/52554569

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值