BZOJ 2301: [HAOI2011]Problem b(莫比乌斯反演,分块,容斥)

来源:互联网 发布:考勤系统数据库设计 编辑:程序博客网 时间:2024/05/26 17:45

题目链接
题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

和HDU1695基本是类似的,这道题如果还是使用之前的方法计算

f(k)=k|du(dk)F(d)=k|du(dk)BdDd
(其中k=1,B,D分别是两个区间右端点)会TLE,这里面有一个优化的地方,因为
Nx2×N
所以就会存在
Nx=Nx+1...
等,这样一段连续的区间就可以分成一块。

比如N=16,当

6d8
的时候,有
166=167=168=2

那么函数
f(1)=1|du(d)F(d)
6d8
的时候F(d)的结果都是一样的,即
F(6)=F(7)=F(8)

那么
f(1)=...+u(6)F(6)+u(7)F(7)+u(8)F(8)+...=...+F(6)[u(6)+u(7)+u(8)]+...

这样u函数用前缀和维护,这段就
O(1)
计算了,所以这段计算的时间由
O(n)
降到
O(N)

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;#define cl(a,b) memset(a,b,sizeof(a))#define ll long long#define pb push_back#define gcd __gcdconst double EPS = 1e-8;const int maxn = 1e5+1000;const int inf  = 0x3f3f3f3f;const double PI = acos(-1.0);bool check[maxn];int prime[maxn];int mu[maxn];void Moblus(){    cl(check,false);    mu[1]=1;int tot=0;    for(int i=2;i<maxn;i++){        if(!check[i]){            prime[tot++]=i;            mu[i]=-1;        }        for(int j=0;j<tot;j++){            if(i*prime[j]>maxn)break;            check[i*prime[j]]=true;            if(i%prime[j]==0){                mu[i*prime[j]]=0;break;            }            else mu[i*prime[j]]=-mu[i];        }    }}int k;/*//TLE 代码ll cal(int n,int m){    n/=k;m/=k;    if(n>m)swap(n,m);    ll ans=0;    for(int i=1;i<=n;i++)ans+=(ll)mu[i]*(n/i)*(m/i);    return ans;}*/ll sum[maxn];//分块优化 N/x值的种类个数不大于2*sqrt(N)ll cal(int n,int m){    n/=k;m/=k;    if(n>m)swap(n,m);    ll ans=0;    for(int i=1,last=0;i<=n;i=last+1){        last=min(n/(n/i),m/(m/i));//计算当前i能往右边扩的位置,比如N=16,i=6,那么就能扩到8的位置        ans+=(sum[last]-sum[i-1])*(n/i)*(m/i);    }    return ans;}int main(){    int T;scanf("%d",&T);    Moblus();    sum[0]=0;for(int i=1;i<maxn;i++)sum[i]=sum[i-1]+mu[i];    while(T--){        int a,b,c,d;        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);        printf("%lld\n",cal(b,d)-cal(a-1,d)-cal(c-1,b)+cal(a-1,c-1));//容斥原理    }    return 0;}
0 0