bzoj 2301(Mobius)

推出式子然后分块求和(还需使用一下容斥原理)。
分析:令f(n,m,i)表示在1<=x<=n,1<=y<=m,满足gcd(x,y)是i的(x,y)的对数。
ans=f(c,d,i)−f(a−1,d,i)−f(b,c−1,i)+f(a−1,c−1,i)其中1<=x<=n,1<=y<=m,满足gcd(x,y)是i的(x,y)的对数也等价于1<=x<=n/i,1<=y<=m/i时(x,y)互质(gcd(x,y)=1)的对数,即
f(n,m,i)=f(n/i,m/i,1)

令F(i)表示满足i|gcd(x,y)的(x,y)的对数。
可以得到一个显然的事实F(i)=⌊ni⌋⌊mi⌋。
根据莫比乌斯反演定理 (不会的可以去看看ACdreamer博客)
F(i)=∑i|df(d)=>f(i)=∑i|dμ(di)F(d)=∑i|dμ(di)⌊nd⌋⌊md⌋
后面O(√n)进行分块求和即可,此处不再赘述

/* 
    ans=calc(b,d,k)-calc(a-1,d,k)-calc(c-1,b,k)+calc(a-1,c-1,k); 
*/ 
#include
using namespace std;
typedef long long ll;
const int MAXN=5e5+2;
int mu[MAXN],prime[MAXN],tot=0,a,b,c,d,k;
bool vis[MAXN];
inline void linear_shaker() {
    mu[1]=1;
    memset(vis,false,sizeof(vis));
    for (register int i=2;iif (!vis[i]) prime[++tot]=i,mu[i]=-1;
        for (int j=1;j<=tot&&i*prime[j]true;
            if (i%prime[j]==0) {mu[i*prime[j]]=0;break;}
            mu[i*prime[j]]=-mu[i];
        }
    }
    for (register int i=2;i1];
}
inline int read() {
    int x=0;char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}
inline ll cal(int n,int m) {
    int t=n>m?m:n,last;ll ret=0;
    for (int i=1;i<=t;i=last+1) {
        last=min(n/(n/i),m/(m/i));
        ret+=1ll*(mu[last]-mu[i-1])*(ll)(n/i)*(m/i);
    }
    return ret;
}
int main() {
    linear_shaker();
    int kase=read();
    while (kase--) {
        a=read(),b=read(),c=read(),d=read(),k=read();
        a=(a-1)/k,b/=k,c=(c-1)/k,d/=k;
        printf("%lld\n",cal(a,c)+cal(b,d)-cal(a,d)-cal(c,b));
    }
    return 0;
}

你可能感兴趣的:(OI-数学)