P1450 [HAOI2008]硬币购物
P1450 [HAOI2008]硬幣購物
題意:
共有 4 種硬幣。面值分別為c1,c2,c3,c4c_1,c_2,c_3,c_4c1?,c2?,c3?,c4?。
某人去商店買東西,去了 n 次,對于每次購買,他帶了 did_idi?枚 i 種硬幣,想購買 s 的價值的東西。請問每次有多少種付款方法。
題解:
第一感覺是母函數(shù),但寫不出來,后來看題解時確實可以做,NTT+多項式求逆+母函數(shù),麻煩而且跑的很慢,不過洛谷可以過,這里不做詳細講解,可以看看代碼
母函數(shù)做法的代碼
我們這里講下完全背包+容斥的做法
如果題目沒有數(shù)量的限制,可以完全背包做法
現(xiàn)在有了數(shù)量限制,我們可以用差分來求,舉個例子:
[2,+∞)?(3,+∞)=[2,+∞)?[4,+∞)=[2,3][2,+∞)?(3,+∞)=[2,+∞)?[4,+∞)=[2,3][2,+∞)?(3,+∞)=[2,+∞)?[4,+∞)=[2,3],顯然只要我們求出[2,+∞)[2,+∞)[2,+∞)和(3,+∞)(3,+∞)(3,+∞),就可以算出[2,3]。可以認為[x,+∞)[x,+∞)[x,+∞)表示價值大于等于x的方案數(shù),可以用完全背包去求。
現(xiàn)在我們用完全背包先預處理出沒有錢數(shù)限制的所有情況,然后減去不合法情況(即某種硬幣超出了所給數(shù)量),得到的不就是答案:
ans=f[s]?∑i=14f[s?ci?(di+1)]ans=f[s]-\sum_{i=1}^{4}f[s-c_i*(d_i+1)]ans=f[s]?∑i=14?f[s?ci??(di?+1)]
現(xiàn)在的任務就是求∑i=14f[s?ci?(di+1)]\sum_{i=1}^{4}f[s-c_i*(d_i+1)]∑i=14?f[s?ci??(di?+1)],可能會認為直接累加不就可以了,并不是,因為i=1,2,3,4,這四部分并不是完全獨立的,換句話說,有可能第一種物品超出限制的同時,第二種也超出了,如果直接累加必然會造成重復計算。這咋整?這個模型很想中學學過的容斥原理:
這個式子應該很熟悉:
三元容斥:
A∪B∪C=(A∪B)∪C=A+B+C?A∩B?A∩C?B∩C+A∩B∩C
擴展一下:
奇加偶減
我們設四種硬幣分別是A,B,C,D,card(X)表示X集合元素個數(shù)
答案=f[s]=card(A?B?C?D)f[s]=card(A\bigcup B\bigcup C\bigcup D)f[s]=card(A?B?C?D)
=f[s]?ans=f[s]-ans=f[s]?ans
ans=card(A)+card(B)+card(C)+card(D)?card(A?B)?card(A?C)?card(A?D)?card(B?C)?card(B?D)?card(C?D)+card(A?B?C)+card(A?B?D)+card(B?C?D)?card(A?B?C?D)ans=card(A)+card(B)+card(C)+card(D)-card(A\bigcap B)-card(A\bigcap C)-card(A\bigcap D)-card(B\bigcap C)-card(B\bigcap D)-card(C\bigcap D)+card(A\bigcap B\bigcap C)+card(A\bigcap B\bigcap D)+card(B\bigcap C\bigcap D)-card(A\bigcap B\bigcap C\bigcap D)ans=card(A)+card(B)+card(C)+card(D)?card(A?B)?card(A?C)?card(A?D)?card(B?C)?card(B?D)?card(C?D)+card(A?B?C)+card(A?B?D)+card(B?C?D)?card(A?B?C?D)
我們剛才說了奇加偶減,所以二進制枚舉(第i位為0表示這個card()種并沒有因為i,否則有),直接從0(0000)枚舉到15(1111)就可以。
本題難點還是在于將完全背包與容斥結合,兩個分開看都不算難
代碼:
#include <bits/stdc++.h> #include <unordered_map> #define debug(a, b) printf("%s = %d\n", a, b); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> PII; clock_t startTime, endTime; //Fe~Jozky const ll INF_ll= 1e18; const int INF_int= 0x3f3f3f3f; void read(){}; template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar) {x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...); } template <typename T> inline void write(T x) {if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0'); } void rd_test() { #ifdef ONLINE_JUDGE #elsestartTime = clock ();freopen("data.in", "r", stdin); #endif } void Time_test() { #ifdef ONLINE_JUDGE #elseendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC); #endif } const int maxn=1e5+9; ll ans,f[maxn]; int c[5];//面值 int d[5];//數(shù)量 void pre(){f[0]=1;for(int i=1;i<=4;i++){for(int j=c[i];j<=100001;j++){f[j]+=f[j-c[i]];}} } int s; void cal(){ans=0;for(int i=1;i<=(1<<4)-1;i++){ll k=0,num=0;for(int j=0;j<4;j++){if(i&(1<<j))//第j位是1{num++;k+=c[j+1]*(d[j+1]+1); } }ll flag=1;if(num%2==0)flag=-1*flag;if(s>=k)ans+=flag*f[s-k];}ans=f[s]-ans; } int main() {//rd_test();for(int i=1;i<=4;i++)cin>>c[i];pre();int t;read(t);while(t--){ans=0;for(int i=1;i<=4;i++)cin>>d[i];cin>>s;cal();cout<<ans<<endl;}//Time_test(); }總結
以上是生活随笔為你收集整理的P1450 [HAOI2008]硬币购物的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CF79D Password(P3943
- 下一篇: FGR是什么意思