题目链接:
BZOJ 2301
题意:
对于给出的
n
个询问,每次求有多少个数对
100%
的数据满足:
1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
题解:
首先用容斥原理将一个询问拆成四个部分,问题等价于询问有多少个数对
(x,y)
满足
1≤x≤floor(n/k),1≤y≤floor(m/k)
且
x
与
那么可以得到:
F(i)=⌊ni ⌋⌊mi ⌋
。
反演后得到:
f(i)=∑ . i|d μ(di )F(d)=∑ . i|d μ(di )⌊nd ⌋⌊md ⌋
。
枚举原题中
k
的每一个倍数,我们就可以
进一步优化,观察式子,发现
⌊nd ⌋
最多有
2n √
个取值。
那么
⌊nd ⌋⌊md ⌋
至多有
2n √ +2m − − √
个取值。
枚举这
2n √ +2m − − √
个取值,对莫比乌斯函数维护一个前缀和,就可以在
O(n √ )
求出解。总时间复杂度
O(nn √ )
。
枚举除法的取值这种方法在莫比乌斯反演的应用中很常用。
就一行代码:
pos=min(n/(n/i),m/(m/i));
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500010;
int a,b,c,d,k;
int tot;
int t;
int mu[maxn],prime[maxn],sum[maxn];
bool check[maxn] = {1,1};
void mobius()
{
mu[1]=1;int n=50000;
for(int i=2;i<=n;i++){
if(!check[i]) prime[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*prime[j]<=n;j++){
check[i*prime[j]]=1;
if(i%prime[j]==0){mu[i*prime[j]]=0;break;}
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
}
int calc(int n,int m)
{
if(n>m) swap(n,m);
int ans=0,pos=0;
for(int i=1;i<=n;i=pos+1)
{
pos=min(n/(n/i),m/(m/i));
ans+=(n/i)*(m/i)*(sum[pos]-sum[i-1]);
}
return ans;
}
int main()
{
mobius();
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
a--;c--;
int ans = calc(b/k,d/k) - calc(a/k,d/k) - calc(c/k,b/k) + calc(a/k,c/k);
printf("%d\n",ans);
}
return 0;
}