[Sdoi2013]项链

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3202

解法:读完题目,很明显我们有两个任务:(1)计算符合提交的颜色数目;(2)计算有m种颜色,长度为n相邻两个颜色不相同的本质不同项链数目(旋转相同被认为是本质相同。)

Part 1:

我们设c2,c3分别为gcd=1的有序整数对以及有序三元组的数目。

那么我们可以得出答案即为m=(c3-(c2-1)*3-1)/6+c2。

计算c2,c3需要莫比乌斯反演的知识,这里直接给出公式好了(不会的同学可以参考《具体数学》)

c2=simga(miu(i)*floor(a/i)^2),c3=simga(miu(i)*floor(a/i)^3),1<=i<=a。

Part 2:

我们设上面所求答案m。

接下来需要用到polya计数相关知识。

我们设f(i)表示长度为i的项链,相邻颜色两两不同的方案数。

那么答案就是sigma(f(gcd(i,n)),1<=i<=n)/n=sigma(phi(d)*f(n/d),d|n)/n。

如何求f(i)?

若第i-1个和第1个的颜色不同,情况数有f(i-1),那么第i个可以放m-2种颜色,方案数为f(i-1)*(m-2)

若第i-1个和第1个的颜色相同,那么i-2肯定和第1个不同,情况数有f(i-2),第i个可以放m-1种颜色。

所以f(i)=f(i-1)*(m-2)+f(i-2)*(m-1)。于是就可以在log(n)的时间求出f(n)了(我的程序里是求过通项的)。

于是这题差不多解决了= =。我们可以通过官方数据了。。

但是。。。vfk大神在bzoj上加了一组n%mod!=0的数据。。这样我们到最后就不能直接乘逆元了。。

于是对于这种情况我们先对答案%mod^2,然后这个结果必然被mod整除,于是我们直接除掉,再乘上(n/mod)的逆元。(n<mod^2,所以这下n/mod的逆元存在了。。)

下面是代码(在bzoj上目前rank1)

/**************************************************************
    Problem: 3202
    User: hta
    Language: C++
    Result: Accepted
    Time:4636 ms
    Memory:93072 kb
****************************************************************/
 
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <string>
using namespace std;
typedef long long LL;
const int maxn=10000003;
const int mod=1000000007;
int A[11],miu[maxn],T,a,sum[maxn];
bool check[maxn];
int totp,prime[maxn/10];
LL N[11],p;
inline int get()
{
    int f=0,v=0;char ch;
    while(!isdigit(ch=getchar()))if(ch=='-')break;
    if(ch=='-')f=1;else v=ch-48;
    while(isdigit(ch=getchar()))v=v*10+ch-48;
    if(f==1)return -v;else return v;
}
 
void init()
{
    T=get();int n=0;
    for(int i=1;i<=T;i++)cin>>N[i],A[i]=get(),n=max(n,A[i]);
    miu[1]=sum[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!check[i])prime[++totp]=i,miu[i]=-1;
        for(int j=1,tp;(tp=prime[j]*i)<=n&&j<=totp;j++)
        {
            check[tp]=1;
            if(i%prime[j]==0){miu[tp]=0;break;}else miu[tp]=-miu[i];
        }
    }
    for(int i=2;i<=n;i++)sum[i]=sum[i-1]+miu[i];
}
/*LL Mul(LL a,LL b)
{
    LL res=0;a%=p,b%=p;
    if(b<0)b+=p;
    for(;b>0;b/=2,a+=a,a-=(a>=p)*p)
        if(b%2==1)res+=a,res-=(res>=p)*p;
    return res;
}*/
 
LL Mul(LL a,LL b)
{
  return (a*b-(LL)((long double)a/p*b)*p+p)%p;
}
void extgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0){x=1,y=0;return;}
    extgcd(b,a%b,x,y);
    LL tp=x;x=y;y=tp-a/b*x;
}
 
LL Pow(LL a,LL b)
{
    LL res=1;
    for(a%=mod;b>0;b/=2,a=a*a%mod)
        if(b&1)res=res*a%mod;
    return res;
}
LL calc(int a)
{
    int i=1,j,tp;
    LL c2=0,c3=0;
    while(i<=a)
    {
        j=a/(a/i),tp=a/i;
        LL tmp=LL(tp)*tp%mod*(sum[j]-sum[i-1])%mod;
        c2=(c2+tmp)%mod,c3+=tmp*tp,c3%=mod;
        i=j+1;
    }
    LL res=(c3-(c2-1)*3-1)%mod*Pow(6,mod-2)+c2;
    res%=mod;if(res<0)res+=mod;
    return res;
}
LL phi(LL n)
{
    LL res=n;
    for(LL i=2;i*i<=n;i++)
    {
        if(n%i!=0)continue;
        res-=res/i;
        while(n%i==0)n/=i;
    }
    if(n>1)res-=res/n;
    return res;
}
LL f(LL n,LL m)
{
    LL res;
    if(n%2==1)res=(Pow(m-1,n)-m+1+mod)%mod;
    else res=(Pow(m-1,n)+m-1+mod)%mod;
    return res;
}
 
 
LL Pow2(LL a,LL b)
{
    LL res=1;
    for(a%=p;b>0;b/=2,a=Mul(a,a))
        if(b&1)res=Mul(res,a);
    return res;
}
LL calc2(int a)
{
    int i=1,j;
    LL c2=0,c3=0,tp;
    while(i<=a)
    {
        j=a/(a/i),tp=a/i;
        LL tmp=Mul(tp*tp%p,sum[j]-sum[i-1]);
        c2=(c2+tmp)%p,c3+=Mul(tmp,tp),c3%=p;
        i=j+1;
    }
    LL x,y;
    extgcd(6,p,x,y);if(x<0)x+=p;
    LL res=Mul((c3-(c2-1)*3-1)%p,x)+c2;
    res%=p;if(res<0)res+=p;
    return res;
}
LL f2(LL n,LL m)
{
    LL res;
    if(n%2==1)res=(Pow2(m-1,n)-m+1+p)%p;
    else res=(Pow2(m-1,n)+m-1+p)%p;
    return res;
}
 
void work(LL n,int a)
{
    if(n%mod==0)
    {
        p=LL(mod)*mod;
        LL col=calc2(a);
        LL ans=0;
        for(LL i=1;i*i<=n;i++)
        {
            if(n%i!=0)continue;
            ans+=Mul(phi(i)%p,f2(n/i,col));
            if(i*i!=n)ans+=Mul(phi(n/i)%p,f2(i,col));
            ans%=p;
        }
        ans/=mod;ans%=mod;
        LL x,y;extgcd(n/mod,p,x,y);
        x%=mod;if(x<0)x+=mod;
        printf("%d\n",int(ans*x%mod));
        return;
    }
    LL col=calc(a);
    LL ans=0;
    for(LL i=1;i*i<=n;i++)
    {
        if(n%i!=0)continue;
        ans+=phi(i)%mod*f(n/i,col);
        if(i*i!=n)ans+=phi(n/i)%mod*f(i,col);
        ans%=mod;
    }
    printf("%d\n",int(ans*Pow(n,mod-2)%mod));
}
 
int main()
{
    init();
    for(int i=1;i<=T;i++)work(N[i],A[i]);
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值