【NOIP2013初赛】整除

from:jack

题目

题目描述

给出n个数a1,a2……an,求区间[L,R]中有多少个整数不能被其中任何一个数整除。

输入

第一行三个正整数,n,L,R。

第二行n个正整数a1,a2……an

输出

一个数,即区间[L,R]中有多少个整数不能被其中任何一个数整除。

样例输入

2 1 1000

10 15

样例输出

867

数据范围

对于30%的数据,1<=n<=10,1<=L,R<=1000

对于100%的数据,1<=n<=18,1<=L,R<=10^9

题目大意

这道题主要是说,在 L L L~ R R R的区间内有多少个数与输入的 a i a_i ai都互质

思路

这道题我的思路使用容斥来做,本来能AC的,模拟赛的时候由于某种原因导致我的时间不够导致 TLE 30。
我们可以先算出能被 a i a_i ai整除的个数,再用再用总数减掉它们。
那问题来了,要求的是 L L L~ R R R区间内的个数, L L L也不一定从1开始,那这怎么处理呢?我们可以考虑用1 ~ R R R区间个数减去1 ~ L L L- 1 1 1 区间个数即为 L L L~ R R R区间的个数。
在这里插入图片描述
如图,上图三个圈的总数为:
三个圈的和(即整体)- 两个圈的交集 + 三个圈的交集
由此,我们可以得到规律:
总数 = 整体 - 两个的交集 + 三个的交集 - 四个的交集 + 五个的交集……

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,l,r,ri,a[20],ans,ans1,tot;
ll f[100005][20],aa[20];
int cnt[20],bz[20];
int gcd(int m,int n)
{
	if(m<n) swap(m,n);
	int r;
	while(n!=0)
	{
		r=m%n;
		m=n;
		n=r;
	}
	return m;
}
ll getans(ll t)
{
	ll s=0;
	for(int i=1;i<=n;i++)
	{
		ll js=0;
		for(int j=1;j<=cnt[i];j++)
			js+=t/(f[j][i]);
		if(i%2==0) s+=js;
		else s-=js;
	}
	return s;
}
void choose(int sh,int x,int k)
{
	if(k>x)
	{
		ll total=1;
		for(int i=1;i<=x;i++)
		{
			int gc=gcd(total,a[aa[i]]);
			total*=a[aa[i]]/gc;
			if(total>r) break;
		}
		if(total<=r)
		{
			++cnt[x];
			f[cnt[x]][x]=total;
		}
	}
	else
	{
		for(int i=sh;i<=n;i++)
			if(!bz[i])
			{
				aa[k]=i;
				bz[i]=1;
				choose(i+1,x,k+1);
				bz[i]=0;
			}
	}
}
int main()
{
	scanf("%lld%lld%lld",&n,&l,&r);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	ri=l-1;
	ans=r;
	ans1=ri;
	for(int i=1;i<=n;i++)
	{
		memset(bz,0,sizeof(bz));
		memset(aa,0,sizeof(aa));
		choose(1,i,1);
	}
	ans1+=getans(ri);
	ans+=getans(r);
	printf("%lld",ans-ans1);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值