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

来源:互联网 发布:卖家在哪开通爱淘宝 编辑:程序博客网 时间:2024/05/21 10:50

好气哦!!!

题目描述

这里写图片描述

输入

这里写图片描述

输出

输出文件 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<cstdio>#include<algorithm>#include<cmath>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 4000000int 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();}

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

0 0