2017.08.12【NOIP提高组】模拟赛B组 巴比伦

####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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值