容斥原理+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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章