[SDOI2014]数表

题意

我们定义 F(i) F ( i ) i i 的约数和,题目要求的是

i=1nj=1mF(gcd(i,j))mod231,F(gcd(i,j))a

我们先忽略 a a 这个限制

g(i)=x=1ny=1m[gcd(x,y)=i]

g(i)=i|dμ(di)ndmd ⇒ g ( i ) = ∑ i | d μ ( d i ) ⌊ n d ⌋ ⌊ m d ⌋

然后就有

Ans=ni=1F(i)g(i) A n s = ∑ i = 1 n F ( i ) g ( i )

=ni=1F(i)i|dμ(di)ndmd = ∑ i = 1 n F ( i ) ∑ i | d μ ( d i ) ⌊ n d ⌋ ⌊ m d ⌋

=nd=1ndmdi|dF(i)μ(di) = ∑ d = 1 n ⌊ n d ⌋ ⌊ m d ⌋ ∑ i | d F ( i ) μ ( d i )

我们令 f(i)=i|dF(i)μ(di) f ( i ) = ∑ i | d F ( i ) μ ( d i )

首先线性筛或者枚举倍数来预处理出 F(i) F ( i )

然后枚举每个数更新倍数,求出 f(i) f ( i ) 的前缀和,分块就好了

考虑有了 a a 的限制,我们发现只有F(i)a i i 才对答案有贡献

我们离线处理,按a排序每次把合法的加入树状数组

就可以维护出 f(i) f ( i ) 的前缀和了,最后分块就好了

因为模数是 2 2 的整数幂,自然溢出就好了,最后要与2311取与

#include<cmath>
#include<cstdio>
#include<algorithm>
#define fp(i,a,b) for(int i=a,I=b;i<=I;++i)
#define fd(i,a,b) for(int i=a,I=b;i>=I;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool chkmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool chkmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
    char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c^48;
    while(c=gc(),47<c&&c<58)x=(x<<1)+(x<<3)+(c^48);x*=y;
}
char sr[1<<21],z[20];int C=-1,Z;
template<class T>inline void we(T x){
    if(x<0)sr[++C]=45,x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
    if(C>1<<20)fwrite(sr,1,C+1,stdout),C=-1;
}
const int N=1e5+5,P=-1u>>1;
typedef int arr[N];
struct que{
    int n,m,a,id;
    inline void in(){sd(n),sd(m),sd(a);if(n>m)swap(n,m);}
    inline bool operator<(const que&b)const{return a<b.a;}
}a[N];
struct no{int val,pos;}f[N];
int T,M;arr c,is,pr,mu,ans,g,s;
inline bool cmp(const no&a,const no&b){return a.val<b.val;}
inline void mdy(int i,int w){for(;i<=M;i+=i&-i)c[i]+=w;}
inline int qry(int i){int w=0;for(;i;i-=i&-i)w+=c[i];return w;}
inline int sol(int n,int m){
    int i=1,j=sqrt(n),s,t=0,w=0;
    for(;i<=j;++i,t=s)s=qry(i),w+=(n/i)*(m/i)*(s-t);
    for(t=qry(i-1);i<=n;i=j+1,t=s){
        j=min(n/(n/i),m/(m/i));s=qry(j);
        w+=(n/i)*(m/i)*(s-t);
    }
    return w;
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    sd(T);fp(i,1,T)a[i].in(),a[i].id=i,chkmax(M,a[i].n);
    mu[1]=f[1].val=f[1].pos=1;
    fp(i,2,M){
        if(!is[i])mu[i]=-1,pr[++pr[0]]=i,s[i]=f[i].val=i+1,g[i]=i;f[i].pos=i;
        for(int j=1,x;j<=pr[0]&&(x=i*pr[j])<=M;++j){
            is[x]=1;
            if(i%pr[j])mu[x]=-mu[i],f[x].val=f[i].val*(1+pr[j]),g[x]=pr[j],s[x]=g[x]+1;
            else{mu[x]=0,g[x]=g[i]*pr[j],s[x]=s[i]+g[x],f[x].val=f[i/g[i]].val*s[x];break;}
        }
    }
    sort(f+1,f+M+1,cmp);sort(a+1,a+T+1);
    for(int i=1,j=1;i<=T;++i){
        for(;j<=M&&f[j].val<=a[i].a;++j)
            for(int k=f[j].pos;k<=M;k+=f[j].pos)
                mdy(k,f[j].val*mu[k/f[j].pos]);
        ans[a[i].id]=sol(a[i].n,a[i].m);
    }
    fp(i,1,T)we(ans[i]&P);
    fwrite(sr,1,C+1,stdout);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值