杭电多校第十场1004(容斥)

2021杭电多校第十场1004(容斥)

Problem - 7080 (dingbacode.com)

题意:

给你两个数n,k统计不超过n的不被前k个质数整除的数字个数

\(1\le n\le 10^{18},1\le k\le16\)

t组询问\(1\le t\le 10^5\)

思路:

考虑最经典的容斥

枚举每个数选还是不选,那么ans+=\((\frac{n}{S})\times(-1)^{the\ number\ of\ selected\ number}\)

即不断地加上选奇数个,减去选偶数个

复杂度为\(O(2^k)\)

由于t比较大,这样显然过不了

考虑当k=8时,其乘积S= 2 3 5 7 11 13 17 19 = 9699690

那么\(ans(n)=ans(9699690)\times\lfloor\frac{n}{9699690}\rfloor+ans(n\ mod\ 9699690)\)

预处理1~9699690即可O(1)查询\(n-\frac{n}{1个}+\frac{n}{2个}\cdots-\frac{n}{7个}+\frac{n}{8个}\)

对于k>8的情况,可以\(O(2^{k-8})\)算出第9到第k个质数的情况,考虑前后相交的情况

显然有\(\lfloor\frac{n}{i\times j}\rfloor=\lfloor\frac{\lfloor\frac{n}{i}\rfloor}{j}\rfloor\)

假设我们枚举后面一段的乘积为res

与前面关联后我们需要求\(-\frac{n}{1个*res}+\frac{n}{2个*res}\cdots-\frac{n}{7个*res}+\frac{n}{8个*res}\),再根据组合后的奇偶判断符号

将res放到分子上,已知我们可以O(1)去求ans(\(\frac{n}{res}\))=\(\frac{n}{res}-\frac{n}{1个*res}+\frac{n}{2个*res}\cdots-\frac{n}{7个*res}+\frac{n}{8个*res}\)

即可以在枚举后半部分时,O(1)处理前后关系

#include<bits/stdc++.h>
using namespace std;
const int maxn=9699690;
int a[1<<17|1],ans1=0,prime[100],bj[110],cnt;
long long c[2*maxn+50];
struct tnode{
	int l,r;
}e[1<<17|1];
bool cmp(tnode l,tnode r)
{
	return l.l<r.l;
}
long long b[1<<17|1];
void get_prime(int x)
{
	for(int i=2;i<=x;i++)
	{
		if(!bj[i])prime[++cnt]=i;
		for(int j=1;j<=cnt&&prime[j]*i<=x;j++)
		{
			bj[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
}
int cal(int x)
{
	int ans=0;
	while(x)
	{
		ans++;
		x-=(x)&(-x);
	}
	return ans;
}
long long get_sum(long long n)
{
	return c[9699690]*(n/9699690)+c[n%9699690];
}
int main()
{
	get_prime(100);
	int t;
	scanf("%d",&t);
	for(int i=1;i<=8;i++)
	{
		a[1<<(i-1)]=prime[i];
	}
	for(int i=1;i<(1<<8);i++)
	{
		for(int j=0;j<8;j++)
		{
			if((i&(1<<j))==0)
			{
				a[i|(1<<j)]=a[i]*prime[j+1];
			}
		}
	}
	for(int i=1;i<=maxn;i++)
	c[i]=1;
	for(int i=1;i<(1<<8);i++)
	{
		if(cal(i)&1)
		{
			for(int j=1;a[i]*j<=maxn;j++)
			{
				c[a[i]*j]-=j;
				c[a[i]*(j+1)]+=j;
			}
		}
		else
		{
			for(int j=1;a[i]*j<=maxn;j++)
			{
				c[a[i]*j]+=j;
				c[a[i]*(j+1)]-=j;
			}
		}
	}
	for(int i=1;i<=maxn;i++)
	c[i]+=c[i-1];
	for(int i=1;i<=8;i++)
	{
		b[1<<(i-1)]=prime[i+8];
	}
	for(int i=1;i<(1<<8);i++)
	{
		for(int j=0;j<8;j++)
		{
			if((i&(1<<j))==0)
			{
				b[i|(1<<j)]=b[i]*prime[j+9];
			}
		}
	}
	while(t--)
	{
		int k;long long n;
		scanf("%lld%d",&n,&k);
		if(k<=8)
		{
			long long ans=n;
			for(int i=1;i<(1<<k);i++)
			{
				if(cal(i)&1)ans-=n/a[i];
				else ans+=n/a[i];
			}
			printf("%lld\n",ans);
		}
		else
		{
			k-=8;
			long long ans=n;
			ans=ans+(get_sum(n)-n);
			for(int i=1;i<(1<<k);i++)
			{
				if(cal(i)&1)
				{
					ans-=n/b[i];
					ans-=(get_sum(n/b[i])-n/b[i]);
				}
				else 
				{
					ans+=n/b[i];
					ans+=(get_sum(n/b[i])-n/b[i]);
				}
			}
			printf("%lld\n",ans);
		}
	}
	return 0;
}
posted @ 2021-08-21 22:38  1427314831a  阅读(78)  评论(0编辑  收藏  举报