[CQOI2017] 小Q的表格

[CQOI2017] 小Q的表格

[题目链接]

链接

[思路要点]

\[ \begin{align} ans &= \sum_{i=1}^{k}\sum_{j=1}^kf(i,j)\\ &= \sum_{d=1}^kf(d)\cdot \sum_{i=1}^{\left[\frac k d\right]} \sum_{j=1}^{\left[\frac k d\right]}[(i,j)==1]\cdot ij \\ \end{align} \]
\[ g(lim, x)=\sum_{i=1}^{lim} \sum_{j=1}^{lim}[(i,j)==x]\cdot ij\],则 \(ans=\sum_{d=1}^kf(d)\cdot g\left(\left[\frac k d\right],1\right)\)

不妨设 \(lim\) 已确定,并且此时 \(g[i]=g(lim,i)\),考虑 \(g[i]\) 怎么求

运用莫比乌斯反演,设
\[ G[i] = \sum_{i|d} g[d] \]

\[ g[i] = \sum _{i|d} \mu\left(\left[\frac d i\right]\right) G(d) \]

\[ \begin{align} G[x] &= \sum _{x|d} g[d]\\ &= \sum_{x|d} \sum_{i=1}^{lim}\sum_{j=1}^{lim}\left[\left(i,j\right)==d\right]\cdot d^2 \\ &= \sum_{i=1}^{lim}\sum_{j=1}^{lim}\left[x \mid \left(i,j\right)\right]\cdot x^2 \\ &= x^2\cdot\sum_{i=1}^{\left[\frac {lim} x\right]} \sum_{j=1}^{\left[\frac {lim} x\right]} ij \end{align} \]
因此我们设 \(S[n]=\sum_{i=1}^{n} \sum_{j=1}^{n} ij=(\frac {n(n+1)} 2)^2\)

则有 \(G[x]=x^2 \cdot S\left[\frac {lim} x\right]\)

那么 \(g[i]= \sum_{i\mid d}\mu\left(\left[\frac d i\right] \right)\cdot d^2 \cdot S\left[\frac {lim} d\right]\)
\[ g[1] = \sum_{d = 1} ^ {lim} \mu (d) \cdot d^2 \cdot S\left[\frac {lim} d\right] \]
此时令 \(g(lim)=g[1]\),则
\[ \begin{align} ans &= \sum_{d=1}^k f(d) \cdot g\left(\left[\frac k d \right],1\right)\\ &=\sum_{d=1}^k f(d) \cdot g\left(\left[\frac k d\right]\right) \end{align} \]
此时问题基本得到解决,我们现在需要在 \(\Theta (\sqrt k)\) 的时间内解决上述式子的求值

运用数论分块,只要维护 \(f\) 的前缀和以及 \(g\) 数组的值即可

对于每次操作一共询问 \(O(\sqrt k)\) 次,修改 \(1\) 次,大致需要 \(O(\sqrt k)\) 修改 \(O(1)\) 查询的数据结构,可以使用分块维护,分别维护块内前缀和以及块尾维护从 \(1\) 到这里的总和。

对于 \(g\) 的求值,可以使用差分
\[ \begin{align} g(n) &= \sum_{i=1} ^n \mu(i) \cdot i^2 \cdot S\left[\frac n i\right]\\ g(n - 1) &=\sum_{i=1} ^{n-1} \mu(i) \cdot i^2 \cdot S\left[\frac {n-1} i\right]\\ g(n)-g(n-1) &= \sum_{i=1}^{n-1} \mu(i) \cdot i^2 \cdot \left(S\left[\frac n i\right] - S\left[\frac{n-1} i\right]\right)+\mu(n)\cdot n^2 \cdot S[1] \end{align} \]
对于后面的 \(\mu(n)\cdot n^2 \cdot S[1]\) 可以直接加上,前面的式子考虑 \(\left(S\left[\frac n i\right] - S\left[\frac{n-1} i\right]\right)\) 什么时候不为 \(0\)

发现 \(\left(S\left[\frac n i\right] - S\left[\frac{n-1} i\right]\right) \ne 0\) 当且仅当 \(i \mid n\),并且此时 \(\left[\frac n i\right] - \left[\frac {n - 1} i\right]=1\)

而由于 \(S[i]=\left(\frac {i(i+1)} 2\right)^2\)


\[ \begin{align} S[x] - S[x - 1] &= \frac {x^2\cdot (x+1)^2}4 - \frac {(x-1)^2\cdot x^2} 4\\ &= x^3 \end{align} \]
因此
\[ \begin{align} g(n)-g(n-1) &= \sum_{i\mid n} \mu(i) \cdot i^2 \cdot \left(S\left[\frac n i\right] - S\left[\frac{n-1} i\right]\right)+\mu(n)\cdot n^2 \cdot S[1]\\ &= \sum _{i \mid n} \mu(i) \cdot i^2 \cdot \left(\left[\frac n i\right]\right)^3 + \mu(n) \cdot n^2 \cdot S[1]\\ &= n^2 \sum_{i\mid n} \mu(i) \cdot \left[\frac n i\right]\\ &= n^2 \varphi(n) + \mu(n) \cdot n^2 \cdot S[1] \end{align} \]
所以 \(g\) 数组可以线性递推解出

因此总复杂度 \(\Theta (n+m\sqrt n)\)

[代码]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int mod=1e9+7;
const int N=5e6;
int a[N],s[N],ss[N],prime[N],p[N],phi[N],cnt,f[N],bl[N],blo,n,m;
void make(int n)
{
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!p[i])prime[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
        {
            int x=i*prime[j];p[x]=1;
            if(i%prime[j])phi[x]=phi[i]*(prime[j]-1);
            else{phi[x]=phi[i]*prime[j];break;}
        }
    }
    for(int i=1;i<=n;i++)f[i]=(f[i-1]+1ll*i*i%mod*phi[i])%mod;

    blo=sqrt(n);
    for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1,a[i]=1ll*i*i%mod;
    for(int i=1;i<=bl[n];i++)
    {
        int l=(i-1)*blo+1,r=min(i*blo,n);s[l]=a[l];
        for(int j=l+1;j<=r;j++)s[j]=(s[j-1]+a[j])%mod;
        ss[i]=(ss[i-1]+s[r])%mod;
    }
}
int query(int n){return (s[n]+ss[bl[n]-1])%mod;}
void update(int x,int w)
{
    int blx=bl[x],r=min(blx*blo,n);
    int tw=(w-a[x]+mod)%mod;a[x]=w;
    for(int i=x;i<=r;i++)s[i]=(s[i]+tw)%mod;
    for(int i=blx;i<=bl[n];i++)ss[i]=(ss[i]+tw)%mod;
}
int solve(int n)
{
    int i=1,lt=sqrt(n);
    int ans=0;

    for(;i<=lt;i++)ans=(ans+1ll*a[i]*f[n/i])%mod;
    for(;i<=n;i=lt+1)
    {
        lt=n/(n/i);
        ans=(ans+1ll*(query(lt)-query(i-1))*f[n/i])%mod;
    }
    return (ans+mod)%mod;
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int main()
{
    scanf("%d%d",&m,&n);
    make(n);
    for(int i=1;i<=m;i++)
    {
        int u,v,k;long long x;
        scanf("%d%d%lld%d",&u,&v,&x,&k);
        int d=gcd(u,v);
        update(gcd(u,v),(x/(u/d)/(v/d))%mod);
        printf("%d\n",solve(k));
    }
}

转载于:https://www.cnblogs.com/wawawa8/p/11097339.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值