【数论】bzoj4174tty的求助

题目链接
(ps:这是一道权限题…)

计算

n=1Nm=1Mk=0m1nk+xm

其中 NM<=5000000<x<=100000 x 精确到小数点后8
位小数。

根本不会的说…
然后去看po姐博客就一脸懵逼辣…

以下摘自popoqqq博客,并加上蒟蒻的辣鸡理解…
单独考虑最后一个和式

k=0m1nk+xm

=k=0m1(nk(mod)m+xm+nkmnk(mod)mm)

上式是考虑 nk m 的倍数部分。

然后分开考虑每一个独立的和式。
d=gcd(n,m),这个变量将贯穿整篇博客。

k=0m1nk(mod)m+xm

这相当于在枚举 m 的剩余系,然后很明显剩余系重复了d次,所以只需要枚举一个剩余系即可。
=dk=0md1kd+xm

和之前同样的考虑方式,考虑 x m的倍数部分。
=d(mdxx(mod)mm+k=0md1kd+x(mod)mm)

很明显, kd+x(mod)m 是不会比 2m 大的…
=d(xx(mod)md+k=0md1[kd+x(mod)mm])

移项
=d(xx(mod)md+k=0md1[kmx(mod)md])

=d(xx(mod)md+mdmx(mod)md)

=d(xx(mod)md+x(mod)md)

=dxd

对于第二个和式:

k=0m1nkm

根据和式的性质,可以将常数提取出来。
=nk=0m1km=nm(m1)2m

=nmn2

对于第三个和式,化简的过程与第一个和式的部分相似。

k=0m1nk(mod)mm

这相当于在枚举 m 的剩余系,然后很明显剩余系重复了d次,所以只需要枚举一个剩余系即可。
=dk=0md1kdm=d2mmd(md1)2

=md2

接着,我们来整理一下这些柿子:

n=1Nm=1M(dxd+nmn2md2)

=12n=1Nm=1M(2dxd+nmnm+d)

把和式展开,其中 Sum(i) 表示从 1 i的公差为 1 的等差数列的前n项和,即 Sum(i)=i(i+1)2
=12(Sum(N)Sum(M)NSum(M)MSum(N)+n=1Nm=1M(2dxd+d))

对于最后的和式中的 d=gcd(n,m) ,我们考虑每一个 d 出现的次数,这样就可以计算每一个相同的d对答案的贡献。
=12(Sum(N)Sum(M)NSum(M)MSum(N)+

d=1min(N,M)(2dxd+d)g=1min(nd,md)μ(g)NdgMdg))

然后我们就枚举 d ,然后用经典的莫比乌斯反演求一下gcd(i,j)==d(1<=i<=N,1<=j<=M)的个数

#include <iostream>
#include <cstdio>
#define LL long long int
#define mod 998244353
#define MAXN 500005
using namespace std;

int n, m, prime[MAXN], tot;
LL u[MAXN];
bool flag[MAXN];
double x;

inline void init()
{
    u[1]=1;
    for(int i=2, j, k;i<MAXN;++i)
    {
        if(!flag[i])prime[++tot]=i, u[i]=-1;
        for(j=1;j<=tot&&(k=i*prime[j])<MAXN;++j)
        {
            flag[k]=1;
            if(i%prime[j]==0){u[k]=0;break;}
            u[k]=-u[i];
        }
    }
    for(int i=1;i<MAXN;++i)
        u[i]=(u[i]+u[i-1]+mod+mod)%mod;
}

LL sum(LL n){return n*(n+1)/2%mod;}

int main()
{
    init();
    scanf("%d%d%lf",&n,&m,&x);
    LL ans=((sum(n)*sum(m)-n*sum(m)-m*sum(n))%mod+mod)%mod, k;
    if(n>m)swap(n,m);
    for(int i=1, nn, mm, last;i<=n;++i)
    {
        k=i+int(x/i)*i*2;
        nn=n/i, mm=m/i;
        for(int j=1;j<=nn;j=last+1)
        {
            last=min(nn/(nn/j),mm/(mm/j));
            ans=(ans+k*(u[last]-u[j-1])%mod*(nn/j)%mod*(mm/j))%mod;
        }
    }
    cout<<ans*499122177%mod<<'\n';
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值