1. 程式人生 > >容斥原理+dfs剪枝 -ost數(WOJ 2592)

容斥原理+dfs剪枝 -ost數(WOJ 2592)

cost數

描述 “給你一個有n個正整數的數列{an}。一個正整數x若滿足在數列{an}中存在一個正整數ai,使x≡17(mod ai),那麼x就是一個‘cost數’。請問1到m的正整數中,有多少個‘cost數’?” 輸入 第一行兩個正整數n和m,意義見問題描述。 第二行n個正整數,分別為數列{an}中的n個數。

輸出 輸出一個整數,表示1到m中“cost數”的個數。

樣例輸入 3 100 18 22 23 樣例輸出 11

【資料範圍】 對於20%的資料,有n≤20,m≤100,000; 另有40%的資料,n≤3,m<2^31; 對於100%的資料,有n≤30,m<231,17<ai<231。 (注:“≡”為同餘符號,a≡b (mod k) 即a mod k = b mod k)

Analysis

考場上推了半天,倒是想出了容斥……奇加偶減n<=3n<=3很好做啊,然後很多很多的怎麼弄啊 後來看題解發現就是dfs看每個數選還是不選,如果這次選出來的數個數為奇,就ans+=(m17)/lcmans+=(m-17)/lcm,否則就減 最後再把ans+1ans+1(17沒有被算)就可以得到答案了 再順手剪剪枝,比如: 當前lcm>mlcm>m時就不用繼續了

還有些細節要注意一下: 比如這樣寫ans+=m/lcm,ans=(n1)ans+=m/lcm,ans-=(n-1)

是錯誤的 具體原因就是因為我們剪了枝,會導致多算的17沒有被減或者減多了

Code
#include<bits/stdc++.h>
#define in read()
#define ll long long
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<
3)+(res<<1)+ch-'0'; ch=getchar(); } return f==1?res:-res; } int n,m,a[40]; ll ans=0; ll gcd(ll x,ll y){ ll z=x%y; while(z){x=y;y=z;z=x%y;} return y; } ll LCM(ll x,ll y){return x/gcd(x,y)*y;} void dfs(int pos,ll lcm,int cnt){ if(pos==0){ if(lcm==1) return;//什麼都不選的時候就沒有意義啦 if(cnt&1) ans+=(m-17)/lcm; else ans-=(m-17)/lcm; return; } if(lcm>m) return; ll tmp=LCM(lcm,a[pos]); if(tmp<=m) dfs(pos-1,tmp,cnt+1); dfs(pos-1,lcm,cnt); } int main(){ n=in;m=in; int i,j,k; for(i=1;i<=n;++i) a[i]=in; sort(a+1,a+n+1); dfs(n,1,0); cout<<ans+1; return 0; }