1. 程式人生 > >LightOJ - 1117 Helping Cicada (求1~n有多少個數不能被這m個數中任意一個整除)(容斥+狀態壓縮)

LightOJ - 1117 Helping Cicada (求1~n有多少個數不能被這m個數中任意一個整除)(容斥+狀態壓縮)

vol == show fine cst href main http color

題意:http://www.lightoj.com/volume_showproblem.php?problem=1117

考慮1個數k,1~n有[n/k]個數能被k整除,[a]表示a向下取整,

所以ans= n-SIGMA([n/num[i]])(1<=i<=m)。再考慮2個數a,b,因為被a整除同時被b整除這部分減了兩次,

所以要加上,ans += n/lcm(a,b),枚舉2個數,又發現3個數的多加了,再減去3個的,再加上4個的,減去5個的,以此類推。

比如m=4

我們就有2的4次方減一 也就是15種方法 可以為二進制的1111 正好對應這m 所以將每一個都便利

然後就是奇數加 偶數減 求出m個數在【1-n】中有多少個倍數

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<vector>
#include<math.h>
#include<string>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define N 106
#define Lson rood<<1
#define
Rson rood<<1|1 int a[N]; LL gcd(LL a,LL b) { return b==0?a:gcd(b,a%b); } int main() { int T,t=1; scanf("%d",&T); while(T--) { LL n,m,sum=0,ans=0; scanf("%lld%lld",&n,&m); for(int i=0;i<m;i++)///狀態壓縮 scanf("%lld",&a[i]);
for(int i=1;i<(1<<m);i++) { LL ans=1; int t=0;///將為一種情況都遍歷出來 for(int j=0;(1<<j)<=i;j++) { if(1<<j&i) { t++; ans=ans*a[j]/gcd(ans,a[j]); } }///根據公式 奇數加 偶數減 if(t%2) sum+=n/ans; else sum-=n/ans; }///sum保存的是在n中有多少個(m個數的倍數) printf("Case %d: %lld\n",t++,n-sum); } return 0; }

LightOJ - 1117 Helping Cicada (求1~n有多少個數不能被這m個數中任意一個整除)(容斥+狀態壓縮)