容斥原理

容斥原理是基本的计数方法。在计数时,我们要做到没有遗漏也没有重复,所以我们推出了一下公式:

如果被计数的事物有A、B两类,那么,A类B类元素个数总和= 属于A类元素个数+ 属于B类元素个数—既是A类又是B类的元素个数。(A∪B = A+B - A∩B) 

我比较懒,后面的情况自己推吧……(*^__^*) 嘻嘻……

下面给一道例题:

硬币购物
难度级别:C; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B
试题描述

现在一共有4种硬币,面值各不相同,分别为ci(i=1,2,3,4)。某人去商店买东西,去了tot次,每次带di枚ci硬币,购买价值为si的货物。请问每次有多少种付款方法。

输入
第一行包括五个数,分别为c1,c2,c3,c4和tot 接下来有tot行,每行五个数,第i+1行五个数依次为第i次购物所带四种硬币的数目和购买货物的价值(d1,d2,d3,d4,s )。各行的数两两之间用一个空格分隔。
输出
一行,包括tot个数,依次为每次付款的方法数。两数之间用一个空格分隔。
输入示例
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
输出示例
4 27
其他说明
数据范围:0<di,s<=100000,0<tot<=1000,数据面值最大不超过20,所给数据保证每次至少有一种付款方法。

首先dp预处理设F[i]为不考虑每种硬币的数量限制的情况下,得到面值i的方案数。则状态转移方程为

F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}

然后容斥原理

代码如下

 1 #include<iostream>
 2 using namespace std;
 3 int c[5],d[5],tot;
 4 long long f[100001],ans;
 5 void work(int now,int num,int s)
 6 {
 7     if(s<0)return ;
 8     if(now==5)
 9     {
10         if(num%2==1) ans-=f[s];
11         else ans+=f[s];
12         return ;
13     }
14     work(now+1,num,s);
15     work(now+1,num+1,s-(d[now]+1)*c[now]);
16 }
17 int main()
18 {
19     scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&tot);
20     f[0]=1;
21     for(int i=1;i<=4;i++)
22         for(int j=c[i];j<=100000;j++) f[j]+=f[j-c[i]];
23     for(int i=1;i<=tot;i++)
24     {
25         int s;
26         scanf("%d%d%d%d%d",&d[1],&d[2],&d[3],&d[4],&s);
27         ans=0;
28         work(1,0,s);
29         if(i==1) printf("%lld",ans);
30         else printf(" %lld",ans);
31     }
32     return 0;
33 }
View Code
O(∩_∩)O~ (*^__^*) 嘻嘻…… O(∩_∩)O哈哈~
原文地址:https://www.cnblogs.com/wls001/p/5162499.html