BZOJ2301 Problem b

莫比乌斯反演+容斥+分块优化

莫比乌斯反演学习资料:

POPOQQQ的莫比乌斯反演论文

那篇论文提及了莫比乌斯反演的第一种形式,另外一种形式可以看莫比乌斯反演定理证明(两种形式)

做这道题时,参考了【莫比乌斯反演】[HYSBZ/BZOJ2301]Problem b

这篇题解里,感觉莫比乌斯反演的推算部分,i和d弄错了,但是之后的分块优化以及代码是对的。可能博主没注意。

//#include<bits/stdc++.h>
#include<stdio.h>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;

const int MAXN=50050;
int p[MAXN],mu[MAXN],sum[MAXN],f[MAXN];

int cal(int n,int m)//计算1<=x<=n&&1<=y<=m的情况下,(x,y)互质的对数 
{
	int limit=min(m,n),last,i,ret=0;
	for(i=1;i<=limit;i=last+1)
	{
		last=min(n/(n/i),m/(m/i));//分块优化,查询(n/i)*(m/i)不变的情况下,i的区间[i,last] 
		ret+=(sum[last]-sum[i-1])*(n/i)*(m/i);//[i,last]区间内,n/i与m/i的值,没有变化,可以通过前缀和计算 
	} 
	return ret;
}

int main()
{
	int i,j,ans,n,a,b,c,d,k,pcnt;
	pcnt=0;
	memset(p,0,sizeof(p));
	memset(f,0,sizeof(f));
	mu[1]=sum[1]=1;
	for(i=2;i<MAXN;i++)//莫比乌斯反演模板 
	{
		if(!f[i])//若为素数 
		{
			p[++pcnt]=i;//记录下素数 
			mu[i]=-1;//u(i)为-1 
		}
		for(j=1;p[j]*i<MAXN;j++)//对i的素数倍数,进行筛排 
		{
			f[p[j]*i]=1;//标记该数为合数 
			if(i%p[j]==0)//若i*p[j]能被p[j]*p[j]整除 
			{
				mu[p[j]*i]=0;//u(i)为0 
				break;//结束循环,之后的暂时没有必要继续计算 
			}
			mu[p[j]*i]=-mu[i];//若p[j]*i包含不同的素数,则u(p[j]*i)=-u(i)
		}
		sum[i]=sum[i-1]+mu[i];//记录前缀和 
	}
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		a--;c--;
		a/=k;b/=k;c/=k;d/=k;
		ans=cal(b,d)-cal(a,d)-cal(b,c)+cal(a,c);//容斥 
		printf("%d\n",ans);
	}
}


发布了280 篇原创文章 · 获赞 0 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览