BZOJ 4330 JSOI2012 爱之项链

Problem G: JSOI2012 爱之项链

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 13  Solved: 5
[Submit][Status][Discuss]

Description

在进香河,流传着这样一段美丽的故事。zyg与kzn是两个生活在进香河的孩子,一天,他们两人闹矛盾了,于是zyg送给了kzn一条精美的爱之项链。从此他们幸福生活在一起。 
这则故事的真实性到今天已经没有意义了,然而我们关注的是那一条精美的爱之项链。这是一条由N个精致的戒指与一块特殊纪念品相连而成的环形,如下图中的爱心符号正是一种特殊纪念品。(据说是2012年情人节时zyg特意托人订制的)上面的每一枚戒指又是由M个带磁性的特殊彩色球状物组成的环形。也许你会认为,这所谓的戒指,更像是一条条小项链。 
下图给出了一种可行的方案,其中左边描述的是单一的一枚戒指,右图描述的是项链。 
 
 
 
这里,所有带磁性的特殊彩色球状物的颜色只有R种,这里我们用1到R来表示。如果一枚戒指可以通过顺时针或逆时针的旋转后与另外一枚戒指相同,则认为这是两枚相同的戒指。 
对于一条爱之项链,要求满足任何相邻两枚戒指必须是不相同的。同时,特殊纪念品左右两枚戒指也必须是不同的。 
现在给定N,M和R,问究竟有多少种不同的爱之项链。 
注意: 
1、特殊纪念品的插入位置不同,也许会得到不同的爱之项链。 
2、这里我们只考虑旋转后是否相同,不考虑翻转操作,这一点不论是对于每一枚戒指,还是对于整条项链,都是这样的。 
 

 

Input

一行,三个正整数,分别是N,M和R。 
 

 

Output

一行,表示有多少种不同的爱之项链。你只需要将答案模3214567。 
 

 

Sample Input

10 5 4

Sample Output

1398595

HINT

 

对于100%的数据,N<=10^15,M<=10^9,R<=10^6。 


题解:先用polya求出戒指种类m,于是问题转化为给你一个大小为n的环和m种颜色,求染色方案使得相邻颜色两两不同!
   •设一个F[n]表示为大小为n的环的方案数
   •求F[n]有两种方式
   •设g[i][0]表示长度为i的开头和末尾相同的序列的个数,g[i][1]表示表示长度为i的开头和末尾不相同的序列的个数。 
     那么最后的答案 f(n)=g[n][1]。f表示的是一个和的形式,但是在求g[n][1]的过程中就已经统计过了长度是n的约数的序列的个数了。 
     比如在统计长度为6的时候,就会把123123这种序列统计进去了。素以这样求是正确的。 
         g[i][0]=g[i1][1] 
     g[i][1]=(o2)g[i][1]+(o1)g[i1][0] 
           =(o2)g[i][1]+(o1)g[i2][1] 
     所f(n)=(o2)f(n1)+(o1)f(n2) 
     这个东西可以矩乘,也可以用特征根求通项。 
     r2=(o2)r+(o1) 
     解r1=1,r2=d1 
     然后f(n)=c1r1n+c2r2n 
     可以把f(1)=0,f(2)=d(d1)带入就可以求出c1,c2 
     解c1=d1,c2=1 
     f(n)=(d1)(1)n+(d1)n
   •于是问题解决,此问题升级版请看: [Sdoi2013]项链

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#define base 3214567
#define ll long long 
using namespace std;
ll n,m,r,ans;
ll read()
{
    ll x=0,f=1; char ch;
    while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
    while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
    return x*f;
}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a; 
}
ll ksm(ll x,ll k){
    ll res=1; for (ll i=k; i; i>>=1,x=1ll*x*x%base) if (i&1) res=1ll*res*x%base; return res;
}
ll phi(ll x)
{
    ll res=x;
    for (int i=2; i<=sqrt(x); i++)
    {
        if (x%i==0) {while (x%i==0) x/=i; res=res*(i-1)/i;}
    }
    if (x!=1) res=res*(x-1)/x;
    return res;
}
ll get(ll N,ll M)
{
    ll ans=0;
    //for (int i=1; i<=N; i++) ans=(ans+ksm(M,gcd(N,i)))%base;
    for (ll i=1; i*i<=N; i++) if (N%i==0) ans=((ans+phi(N/i)*ksm(M,i))%base+((i*i!=N)?phi(i)*ksm(M,N/i):0))%base;
    return ans;
}
ll get2(ll N,ll M)
{
    //cout<<" "<<N<<" "<<M<<endl;
    ll ans=0;
    //ans=ksm(M,N+(gcd(N,i)%2)?1-M:M-1)); 
    ans=(ksm(M-1,N)+(N%2==0?M-1:1-M))%base;
    return ans;
}
int main()
{
    n=read(); m=read(); r=read();
    m=get(m,r)*ksm(m,base-2)%base;
//  cout<<m<<endl;
    ans=get2(n,m);
    printf("%d\n",ans);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/HQHQ/p/5952914.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值