[bzoj3202][SDOI2013]项链

3202: [Sdoi2013]项链

Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 392 Solved: 135
[Submit][Status][Discuss]
Description

项链是人体的装饰品之一,是最早出现的首饰。项链除了具有装饰功能之外,有些项 链还具有特殊显示作用,如天主教徒的十字架
链和佛教徒的念珠。 从古至今人们为了美化人体本身,也美 化环境,制造了各种不同风格,不同特点、不同式样的项链,满足了不同肤色、不同民族、不同审美观的人的审美需要。就材料而论,首
饰市场上的项链有黄金、白银、珠宝等几种。珍珠项链为珍珠制成的饰品,即将珍珠 钻孔后用线串在一起,佩戴于项间。天然珍珠项链具有一定的护养作用。

最近,铭铭迷恋上了一种项链。与其他珍珠项链基本上相同,不过这种项链的珠子却 与众不同,是正三菱柱的泰山石雕刻而成的。三菱柱的侧面是正方形构成的,上面刻有数字。 能够让铭铭满意的项链必须满足下面的条件:
1:这串项链由n颗珠子构成的。
2:每一个珠子上面的数字x,必须满足0 < x <= a,且珠子上面的数字的最大公约数要恰 好为1。两个珠子被认为是相同的,当且仅当他们经过旋转,或者翻转后能够变成一样的。 3:相邻的两个珠子必须不同。
4:两串项链如果能够经过旋转变成一样的,那么这两串项链就是相同的! 铭铭很好奇如果给定n和a,能够找到多少不同串项链。由于答案可能很大,所以对输 出的答案mod 1000000007。

Input

数据由多组数据构成: 
第一行给定一个T<=10,代表由T组数据。 
接下来T行,每行两个数n和a。 

Output

对于每组数据输出有多少不同的串。 

Sample Input

1 

2  2

Sample Output

3 

HINT

对于100%的数据:所有的n<=10^14,a<=10^7,T<=10; 

样例解释:由三种珠子:[1,1,1],[1,1,2],[1,2,2].组成的串有:[1,2],[1,3],[2,3]。

先不考虑题目中所说的 gcd=1 的情况,那么就是从可重元素中取k个有多少种取法。因为可以重复选,所以多加入 k 个元素,就相当于要把这n+k个元素分成 n 份有多少种取法。可以用插板法,在n+k1个空中找 n1 个空。就是 C(n+k1,n1)
也就等价于我们第一问要求的珠子的种数 o=C(n+2,3)
然后再把 gcd!=1 的充斥掉:那就是减去1个质数的,加上2个质数的……,那么最后就是用莫比乌斯函数容斥一下: o=d|aμ(d)C(ad+2,3)
然后就应该用到群论的知识了,由于不会群论,所以听了大神莫比乌斯反演的方法。
F(x)x
f(x)=i|xF(x)
那么最后的答案就是 ans=d|nF(d)d 因为旋转之后相同的算一种,所以一个长度为d的会被统计d次。
ans=d|nF(d)d
      =d|np|dμ(p)f(dp)d
      =d|n1dp|dμ(p)f(dp)
      =t|np|nt1ptμ(p)f(t)
      =t|nf(t)tp|ntμ(p)p
φ(n)=d|nμ(d)nd
p|ntμ(p)p=φ(nt)nt
ans=t|nf(t)tφ(nt)nt
      =1nt|nf(t)φ(nt)
现在我们只需要求出f(t)就行了。
设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(0)=0,f(1)=n(n1) 带入就可以求出 c1,c2
c1=d1,c2=1
f(n)=(d1)(1)n+(d1)n
直接dfs求出所有的约数算就可以了。
但是这个题vfk加强了数据!!!可能会有n是p的倍数的情况,也就是n没有逆元。怎么办呢?
因为 xy%p=x%ypy
我们看要求的答案是 t|nf(t)φ(nt)n%p 用x代替 那么就是
ans=xn%p=xp%p2np%p

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define O 1000000007
#define LL long long
const int N=10000000;
bool flag[N+10];
LL o,n,ni,phi,six,D=1e9+7,p[N],ans,use[N];
int prime[N+10],u[N+10],T,m,mi[N];
inline void prepare(){
    int i,j;
    for(u[1]=1,i=2;i<=N;++i){
        if(!flag[i]){
            prime[++prime[0]]=i;
            u[i]=-1;
        }
        for(j=1;j<=prime[0]&&i*prime[j]<=N;++j){
            flag[i*prime[j]]=true;
            if(i%prime[j]==0){
                u[i*prime[j]]=0;
                break;
            }
            u[i*prime[j]]=-u[i];
        }
    }
    for(i=2;i<=N;++i) u[i]+=u[i-1];
}
inline LL quickmul(LL x,LL y){
    LL sum=0,v=1;
    if(y<0LL) y=-y,v=-1;
    while(y){
        if(y&1LL) sum=(sum+x)%D;
        y>>=1LL;x=(x+x)%D;
    }
    return sum*v;
}
inline LL quickpow(LL x,LL y){
    LL sum=1LL;
    while(y){
        if(y&1L) sum=quickmul(sum,x);
        y>>=1LL;x=quickmul(x,x);
    }
    return sum;
}
inline LL C(int x){
    return quickmul(quickmul(x,x-1),quickmul(x-2,six));
}
inline LL calc(){
    int i,j;
    LL sum=1,f,PHI=1;
    for(i=1;i<=p[0];++i)
      for(j=1;j<=use[i];++j) sum=sum*p[i];
    for(PHI=sum,i=1;i<=p[0];++i)
      if(use[i]) PHI=PHI/p[i]*(p[i]-1);
    sum=n/sum;
    f=(((o-1LL)*(LL)(sum%2?-1:1)+quickpow((LL)(o-1),sum))+D)%D;
    return quickmul(PHI,f);
}
inline void dfs(int x){
    int i;
    if(x==p[0]+1){
        ans=(ans+calc())%D;
        return ;
    }
    for(i=0;i<=mi[x];++i)
      use[x]=i,dfs(x+1);
}
int main(){
    prepare();
    scanf("%d",&T);
    while(T--){
        LL pre=O;D=O;
        int i,pos,mark=0;
        scanf("%lld%d",&n,&m);
        if(n%D) phi=D-1,ni=quickpow(n,D-2);
        else ni=quickpow(n/D,D-2),D=D*D,phi=quickmul(pre,pre-1),mark=1;
        six=quickpow(6LL,phi-1);
        for(o=0,i=1;i<=m;i=pos+1){
            pos=m/(m/i);
            o=(o+(quickmul((LL)(u[pos]-u[i-1]),C(m/i+2))+D)%D)%D;
        }
        LL x=n;
        for(p[0]=mi[0]=0,i=1;i<=prime[0]&&x>1LL;++i){
            if(x%prime[i]==0){
                p[++p[0]]=prime[i];
                mi[++mi[0]]=0;
                while(x%prime[i]==0){
                    ++mi[mi[0]];
                    x/=prime[i];
                }
            }
        }
        if(x>1LL) p[++p[0]]=x,mi[++mi[0]]=1;
        ans=0;dfs(1);
        if(mark) ans/=pre;
        ans=quickmul(ans,ni);
        printf("%lld\n",ans%pre);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值