1. 程式人生 > 其它 >[HAOI2008]硬幣購物 題解

[HAOI2008]硬幣購物 題解

這題目我覺得很強的啊……

書歸正傳,這道題有一個顯然的多重揹包做法,但是很遺憾,這題過不去,時間複雜度是

那我們不妨先考慮一個簡單的東西,就是沒有硬幣數量的限制,那麼完全揹包就能秒了這個題

那有了限制之後呢?

那麼發現硬幣的數量只有4種,是個很好的性質

考慮容斥解決,對於第 \(j\) 個硬幣,其實他的答案就是拋掉多餘硬幣個數的,也就是題中的 \(d_j\)

實際上,我們應該拋掉 \(d_j +1\) 個的,因為可以不選,那麼就相當於減去 \(f[s-(d_j +1) \times c_j]\) ,答案就是 \(f[s]-f[s-(d_j +1) \times c_j]\)

如果算四個這玩意,就會算少,包括同時超過兩個限制條件的等等,因此,就可以容斥了,具體的大概就是奇數減,偶數加

程式碼如下

#include<bits/stdc++.h>
const int N=100010;
using namespace std;
int T,c[10],d[10],s;
long long ans=0,f[N]={1,0};
void bag(){for(int i=1;i<=4;i++) for(int j=c[i];j<=N-1;j++) f[j]+=f[j-c[i]];}
void dfs(int x,int k,int sum){
	if(sum<0) return ;
	if(x==5){
		if(k&1) ans-=f[sum];
		else ans+=f[sum];
		return ;
	}
	dfs(x+1,k+1,sum-c[x]*(d[x]+1)),dfs(x+1,k,sum);
}
int main(){
	scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&T);
	bag();
	while(T-->0){
		scanf("%d%d%d%d%d",&d[1],&d[2],&d[3],&d[4],&s);
		ans=0;
		dfs(1,0,s);
		printf("%lld\n",ans);
	}
	return 0;
}