BZOJ4174: tty的求助

42 篇文章 0 订阅

一开始一直觉得是搞掉一个 然后用类欧做然后就不会了qaq
看了题解发现跟类欧完全没关系qaq


便[x]x 为 了 方 便 以 下 用 [ x ] 代 表 ⌊ x ⌋
我们要求这么个东西

n=0Nm=0Mk=0M1[nk+xm] ∑ n = 0 N ∑ m = 0 M ∑ k = 0 M − 1 [ n k + x m ]

给的这个x可以直接当成整数看,小数点后面的东西没用


我们先来考虑确定了 n,m n , m 的子问题

k[nk+xm]=k[nkmodm+xm]+knknkmodmm ∑ k [ n k + x m ] = ∑ k [ n k mod m + x m ] + ∑ k n k − n k mod m m

对于拆出来的第一项
如果我们令 d=(n,m) d = ( n , m ) ,那么前面这个东西 m1k[nkmodm+xm] ∑ k m − 1 [ n k mod m + x m ] 发现他在  mod m   m o d   m 下有个长度为 md m d 的循环节
那么它就变成了 dmd1k=0kdmodm+xm d ∑ k = 0 m d − 1 k d mod m + x m
接着把x也拆出来
d(kkdmodm+xmodmm+xxmodmm) d ( ∑ k k d mod m + x mod m m + x − x mod m m )

两个< m的数加起来< 2m,所以第一项只可能是0或1,于是
d(k[kdmodm+xmodm>=m]+xxmodmm) d ( ∑ k [ k d mod m + x mod m >= m ] + x − x mod m m )

k[kdmodm+xmodm>=m]  = [xmodmd] ∑ k [ k d mod m + x mod m >= m ]     =   [ x mod m d ] ,这个式子可以理解成小于等于x的每个 ad(a>0) a d ( a > 0 ) 都能在 kd k d 中找到一个 k k 与之匹配相加>=m,反之同理,那么kd集合的匹配数就是 [xmodmd] [ x mod m d ]
md1k=0xxmodmm=xxmodmd ∑ k = 0 m d − 1 x − x mod m m = x − x mod m d
于是有

d([xmodmd]+xxmodmd) d ( [ x mod m d ] + x − x mod m d )

也即 d[xd] d [ x d ]

接着化简第二项 m1k=0nknkmodmm ∑ k = 0 m − 1 n k − n k mod m m

=n(m1)2dmmd1k=0kd = n ( m − 1 ) 2 − d m ∑ k = 0 m d − 1 k d
=n(m1)(md)2 = n ( m − 1 ) − ( m − d ) 2

那么我们就可以在知道 d d 的情况下O(1)解决这个子问题了
将他带回原问题


nmd[xd]+n(m1)(md)2 ∑ n ∑ m d [ x d ] + n ( m − 1 ) − ( m − d ) 2

就是个简单的反演,推一下, nlogn n l o g n 调和级数暴力预处理一个 f(i) f ( i ) 应该就行了
我当时写的时候中间有一步推错了所以代码处理了两个函数 f(i),g(i) f ( i ) , g ( i ) ,后来改过来仍然没有删掉另一个函数就顺着改一下那样做了,代码不推荐食用

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 510000;
const int mod = 998244353;
inline void add(int &a,const int &b){a+=b;if(a<0)a+=mod;if(a>=mod)a-=mod;}

int pw(int x,int k)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
        re=(ll)re*x%mod;
    return re;
}

int n,m,X,u,inv2;
int p[maxn],pri,mu[maxn];
int sum[maxn],f[maxn],g[maxn];
bool v[maxn];
void pre()
{
    mu[1]=1;
    for(int i=2;i<=u;i++)
    {
        if(!v[i]) p[++pri]=i,mu[i]=-1;
        for(int j=1,k=p[j]*i;k<=u;j++,k=p[j]*i)
        {
            v[k]=true;
            if(i%p[j]==0) break;
            mu[k]=-mu[i];
        }
    }
    for(int i=1;i<=u;i++)
    {
        int t2=(ll)i*inv2%mod;
        int t1=((ll)i*(X/i)%mod+t2)%mod;
        for(int j=i,k=1;j<=u;j+=i,k++)
            add(f[j],t1*mu[k]),add(g[j],(ll)t2*mu[k]*k%mod);
    }
    for(int i=1;i<=u;i++) sum[i]=(sum[i-1]+i)%mod;
}

int ans;

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    inv2=pw(2,mod-2);

    double temp;
    scanf("%d%d%lf",&n,&m,&temp); X=(int)temp;
    u=max(n,m);
    pre();

    ans=(ll)m*(m-1)%mod*inv2%mod*inv2%mod*sum[n]%mod;
    u=min(n,m);
    for(int i=1;i<=u;i++) add(ans,(ll)f[i]*(n/i)%mod*(m/i)%mod);
    for(int i=1;i<=u;i++) add(ans,-(ll)g[i]*(n/i)%mod*sum[m/i]%mod);
    printf("%d\n",ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值