####Description/Input/Output
####Sample Input/Sample Output/Data Constraint
题解:
本题咋看上去是一道裸的DP呢??在认真看。发现DP不可做(db都知道)
于是我们把题意简化一下:
有n种宝具,其中有T种是神器且有限,bi表示第i个神器数量不超过bi件。
若是相同的宝具数量相同,就是相同的搭配方案。求方案数 mod p。当然,选0件也是方案。
然后我们不急于想本题,我们来看看另一个题目:
有x个苹果,放进y个篮子中,但是篮子不一定每个都放。
显然,方案数为C(y-1,x+y-1)=C(x,x+y-1)
但是不限定放x个苹果的话,那么方案数为C(0,0+y-1)+C(1,1+y-1)+……+C(x,x+y-1)
化简得:C(0,y-1)+C(1,y)+C(2,y+1)+……+C(x,x+y-1)
根据一个神奇的公式C(n,m)=C(n-1,m-1)+C(n,m-1)
然后上式就变成了C(x,x+y)
算出这个之后,我们在回到原题,发现这样直接套上去是有问题的,为什么??因为题目中T个物品有限制,这个方案数里包含一些超标的(比如说一个物品有3个,选了4个即为超标)。那么我们考虑容斥原理。
容斥原理是什么呢??
例:
一次期末考试,某班有15人数学得满分,有12人语文得满分,并且有4人语、数都是满分,那么这个班至少有一门得满分的同学有多少人?
分析
依题意,被计数的事物有语、数得满分两类,“数学得满分”称为“A类元素”,“语文得满分”称为“B类元素”,“语、数都是满分”称为“既是A类又是B类的元素”,“至少有一门得满分的同学”称为“A类和B类元素个数”的总和。
答案
15+12-4=23
这是最简单的东东。
我们来看个图更好理解:
求有颜色的面积
于是面积为:
红色球面积+黄色球面积+蓝色球面积-红色与黄色重叠-黄色与蓝色重叠-红色与蓝色重叠+红色与蓝色与黄色重叠。
显而易见。
T<=15,所以递归暴力枚举选哪些一定会超标。
怎样保证一个物品一定超标?
假设此物有xi个,至少选xi+1个才保证超标。
所以暴力枚举一个物品是否超标,把xi+1加进sum里,最后超标的方案数为
C(m-sum,n+m-sum)。解释:这就是已经选了sum个物品,剩下m-sum个物品,放进n个篮子里且不一定放完的方案数,而且一定合法。
那么,本体就直接
ans=原方案数-一个物品超标+两个超标-三个超标…
但是我们会发现直接C来算会时间炸掉,而且用快速幂也会超。然后就引入一个特别厉害的公式:
C(n,m) mod p=(C(n mod p,m mod p)*C(n div p,m div p)) mod p
这样可以递归来求,当然有一些边界条件需要注意,你也可以预处理出阶乘与用快速幂。因为你求的时候可以用费马小定理。
这道题就被完美地解出来了。
Code:
var
i,j,k,l,n,m,t,q,p:longint;
answer,ans,jl:int64;
jc:array[0..100000] of longint;
bh,a:array[0..20] of longint;
function qsm(a,b,n:int64):int64;
var
t,y:int64;
begin
t:=1; y:=a;
while b<>0 do
begin
if(b and 1)=1 then t:=t*y mod n;
y:=y*y mod n;
b:=b shr 1;
end;
exit(t);
end;
function c(n,m:longint):longint;
begin
if (n<0)or(m<0) then exit(0)
else if n=0 then exit(1)
else if n>m then exit(0)
else if n=m then exit(1)
else
if (n<=p)and(m<=p) then
begin
exit(jc[m]*qsm(jc[m-n]*jc[n] mod p,p-2,p) mod p);
end
else
exit(c(n mod p,m mod p)*c(n div p,m div p) mod p);
end;
procedure dg(dep,num,sum:longint);
begin
if dep>t then
begin
bh[num]:=(bh[num]+c(m-sum,n+m-sum)) mod p;
end
else
begin
dg(dep+1,num+1,sum+a[dep]+1);
dg(dep+1,num,sum);
end;
end;
begin
assign(input,'babylon.in');reset(input);
assign(output,'babylon.out');rewrite(output);
readln(n,t,m,p);
jc[1]:=1;
for i:=2 to p do
begin
jc[i]:=(jc[i-1]*i) mod p;
end;
for i:=1 to t do
begin
readln(a[i]);
end;
dg(1,0,0);
ans:=c(m,n+m);
for i:=1 to t do
begin
if i mod 2=1 then
begin
ans:=(ans-bh[i]+p) mod p;
end
else
begin
ans:=(ans+bh[i]) mod p;
end;
end;
writeln(ans);
close(input);close(output);
end.