ZOJ 3233 Lucky Number --容斥原理

时间:2023-04-29 15:16:50

这题被出题人给活活坑了,题目居然理解错了。。哎,不想多说。

题意:给两组数,A组为幸运基数,B组为不幸运的基数,问在[low,high]区间内有多少个数:至少被A组中一个数整除,并且被B中任意一个数整除。|A|<=15.

分析:看到A长度这么小,以及求区间内满足条件的个数问题,容易想到容斥原理,因为不被B中任意一个数整除,所以将B数组所有数取一个最小公倍数LCM,那么就变成了幸运数字都不会被这个LCM整除。

然后枚举子集,实现要将A中元素去除相互整除的情况,比如A = [2,4],这时因为被至少一个数整除就行,那么一个2就可以满足了,将4去掉。

设A1 = {区间内被a1整除的数},A2 = {区间内被a2整除的数},...An = {区间内被an整除的数}

那么由于:ZOJ 3233 Lucky Number --容斥原理

然后还要处理不被LCM整除的情况,设Bi = {区间内被 i 整除的数},则要减去的数为:

ZOJ 3233 Lucky Number --容斥原理(cnt为数的个数)

所以就可以做容斥了。

一段区间[low,high]内被k整除的数的个数为: high/k-(low-1)/k

注意处理爆long long的情况

代码: (0ms)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
#define N 100007 ll a[],aa[];
ll b[];
int vis[]; ll gcd(ll a,ll b)
{
if(!b)
return a;
return gcd(b,a%b);
} int calc(int S) //计算数的个数
{
int cnt = ;
while(S)
{
if(S&)
cnt++;
S >>= ;
}
return cnt;
} int main()
{
int n,m,i,j;
ll low,high;
ll cnt;
int S;
while(scanf("%d%d%lld%lld",&n,&m,&low,&high)!=EOF)
{
if(n == && m == && low == && high == )
break;
for(i=;i<n;i++)
scanf("%lld",&a[i]);
for(i=;i<m;i++)
scanf("%lld",&b[i]);
memset(vis,,sizeof(vis));
sort(a,a+n);
for(i=;i<n;i++)
{
if(vis[i])
continue;
for(j=i+;j<n;j++)
{
if(vis[j])
continue;
if(a[j]%a[i] == ) //去除冗余
vis[j] = ;
}
}
int ka = ;
for(i=;i<n;i++)
{
if(!vis[i])
aa[ka++] = a[i];
}
ll lcm = 1LL;
int tag = ;
for(i=;i<m;i++) //求LCM
{
lcm = lcm*b[i]/(gcd(lcm,b[i]));
if(lcm > high || lcm < )
{
tag = ;
break;
}
}
if(!tag) //爆出,不管
lcm = high+;
S = (<<ka)-; //总状态数
ll ans = ;
for(int state=;state<=S;state++)
{
int c = calc(state);
int sign = (c%?1LL:-1LL); //根据个数定符号
int tmp = state;
i = ;
ll antibase = 1LL;
ll base = 1LL;
int flag = ;
while(i<ka) //子集
{
if(tmp&)
{
base = base/gcd(base,aa[i])*aa[i];
if(base > high || base < )
{
flag = ;
break;
}
}
tmp>>=;
i++;
}
if(!flag)
continue;
ans += sign*(high/base-(low-1LL)/base);
if(!tag) //LCM爆范围,不管
continue;
antibase = base/(gcd(base,lcm))*lcm;
ans -= sign*(high/antibase-(low-1LL)/antibase);
}
printf("%lld\n",ans);
}
return ;
}

(有发现不对的地方欢迎评论指出)