【数论】[CQOI2017]小 Q 的表格

好气哦!!!

题目描述

输入

输出

输出文件 table.out。
输出共m行,每次操作之后输出1行,表示答案mod 1,000,000,007之后的结果。

分析

好气啊!!!好气啊!!这样一个傻逼题为什么当时不A
观察式子 (a,a+b) 可以发现,我们可以根据 gcd 给表格的元素分组。每次修改一个一个数 (i,j) ,和它同组的元素都会修改。
对于每一组,比如第 g 组, f(i,j)=f(g,g)(i/g)(j/g) ,第g组的和为 S(g)

S(g)=f(g,g)×i=1kgij=1kg[gcd(i,j)]j=f(g,g)×(2×i=1kgiji[gcd(i,j)]j)=f(g,g)×(2×i=1kgi×i×ϕ(i)+[i==1]2)=f(g,g)×i=1kgϕ(i)×i×i

然后考场上我就在第二个等式之后卡住咯。。。
求出 f(g,g) 的前缀和和 phi(i)×i×i 的前缀和我们就可以分块优化。由于查询操作是修改操作的 n 倍,我们使用分块来维护前缀和,时间复杂度为 O(n+m(n))

代码

#include
#include
#include
template<class T>
void Read(T &x){
    char c;
    bool f(0);
    while(c=getchar(),c!=EOF)
        if(c=='-')
            f=1;
        else if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            if(f)
                x=-x;
            return;
        }
}
#define MOD 1000000007
#define MAXN 4000000
int p[MAXN+10],pcnt,sk[MAXN+10],phi[MAXN+10],bl[MAXN+10];
int n,m;
bool f[MAXN+10];
void prepare(){
    int i,j;
    phi[1]=1;
    sk[1]=1;
    for(i=2;i<=n;i++){
        if(!f[i]){
            phi[i]=i-1;
            p[++pcnt]=i;
        }
        for(j=1;i*p[j]<=n;j++){
            f[i*p[j]]=1;
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }
            phi[i*p[j]]=phi[i]*phi[p[j]];
        }
        sk[i]=(sk[i-1]+1ll*phi[i]*i%MOD*i)%MOD;
    }
}
long long tms[MAXN+10],in[MAXN+10],out[MAXN+10];
int gcd(int a,int b){
    int t;
    while(b){
        t=a%b;
        a=b;
        b=t;
    }
    return a;
}
int lowbit(int x){
    return x&-x;
}
void update(int x,int d){
    for(int i=x;bl[x]==bl[i];i++)
        in[i]+=d;
    for(int i=bl[x]+1;i<=bl[n];i++)
        out[i]+=d;
}
long long get_sum(int x){
    return out[bl[x]]+in[x];
}
void read(){
    Read(m),Read(n);
    int t=sqrt(n);
    for(int i=1;i<=n;i++)
        bl[i]=(i+t-1)/t;
    for(int i=1;i<=n;i++)
        in[i]=in[i-1]+(tms[i]=(1ll*i*i)%MOD);
}
int Sum(int n){
    return 1ll*n*(n+1)/2%MOD;
}
void solve(){
    long long i,a,b,k,d;
    long long x;
    while(m--){
        Read(a),Read(b),Read(x),Read(k);
        d=gcd(a,b);
        update(d,-tms[d]);
        tms[d]=x/(1ll*a*b/d/d)%MOD;

        update(d,tms[d]);
        long long tt;
        long long ans=0;
        for(i=1;i<=k;i=tt+1){
            tt=k/(k/i);
            long long ttt=(get_sum(tt)-get_sum(i-1))%MOD,ret(0);
            ret=sk[k/i];
            ans=((ans+ttt*ret)%MOD+MOD)%MOD;
        }
        printf("%lld\n",ans);
    }
}
int main()
{
    read();
    prepare();
    solve();
}

我辜负了出题人的良苦用心。

你可能感兴趣的:(数论,c++,CQOI2017,数论,欧拉函数,线性筛)