zoukankan      html  css  js  c++  java
  • 【2016北京集训】crash的游戏

    Portal --> broken qwq

    Description

      有个口袋,一开始里面有(N)个球,接下来进行(M)次操作,每次可以选择往里面放一个球或者从里面拿一个球出来,在这(M)次操作之后,要取(K)个球出来,对于每一种操作方式,都有取出(K)个球的方案数(球两两不同),求方案总数

    ​  数据范围:(T<=500,N<=10^9,M<=10^9,K<=300),保证(N>=M+K)

      

    Solution

    ​  首先考虑最朴素的做法,我们可以考虑在(M)次操作中有(i)次是放球的,(M-i)次是取球的,那么可以得到这样的一个式子:

    [ans=sumlimits_{i=0}^minom m iinom {n-m+2*i} K ]

    ​  然后看一下数据范围:哦豁凉凉

    ​  显然我们需要探求一个时间复杂度只与(K)相关的算法,所以我们换一个角度思考问题

      考虑转化一下这个操作:假设我们一开始先拿走(M)个球(因为数据有保证所以不用担心不够拿的问题),那么接下来的每次操作就变成了:要么不动,要么往袋子里面加入(2)个球

    ​  这样一来在操作结束之后,我们最终的球可以按照来源分为两类:一类是原本的(N-M)个球(称为第一类),一类是后面操作中加入的球(称为第二类),现在我们要在这些球中取(K)

    ​  假设这(K)个球中,有(x)个是第一类中取的,有(K-x)个是第二类中取的,第一类的贡献显然是“从(N-M)个中选(x)个”也就是(inom {N-M} x),接下来考虑第二类的贡献怎么算

    ​  考虑dp,每一次操作是加入一对球,那么我们设(f[i][j])表示:我们拿走了(i)个球,并且这(i)个球属于的加入操作的集合大小为(j)(也就是说选了(j)个加入操作中加入的球,如果说有一次操作加入的两个球都被拿走了,那么集合大小还是(1))的取球方案数,不难列出递推式:

    [f[i][j]=2*f[i-1][j-1]+f[i-2][j-1] ]

      具体一点就是,前半部分是在这次操作中取(1)个球,可以选择这次操作加入的第一个球或者第二个球(球两两之间不同嘛);后半部分是将这次操作中的(2)个球都取上

      那么在第二类中取(x)个球的贡献就是

    [sumlimits_{j=lceilfrac{x}{2} ceil}^xinom M j f[x][j]cdot 2^{M-j} ]

    ​  具体一点就是:枚举涉及的加入操作集合大小(j),然后要在(M)个操作中钦定(j)个操作为加入操作,然后(f[x][j])就是贡献,剩下还有(M-j)个操作,那么这些操作不管是加球还是什么都不做都可以,所以是(2^{M-j})

    ​  所以总的式子就是:

    [ans=sumlimits_{i=0}^Ksumlimits_{j=lceilfrac{K-i}{2} ceil}^{K-i}inom {N-M}{i}inom M j f[K-i][j]cdot 2^{M-j} ]

      中间的两个组合数的话。。不难发现从(inom n m)推到(inom n {m+1})只要乘上一个(frac{n-m}{m+1})即可,所以我们可以一路递推上去就好了

    ​  于是乎就可以(O(K^2))搞定这题啦ovo

      
      然而ckw大爷有不同的做法qwq实际上这题可以简单粗暴直接推式子但是qwq我这种蒟蒻推不动啊qwq

      不过。。mark:我们可以将组合数(inom x i)看成一个关于(x)(i)次多项式(因为写成阶乘相除形式之后拆个括号就很明显了)

    ​  
      代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=310,MOD=1e9+7;
    int f[N][N],inv[N];
    int n,m,K,T,ans;
    int plu(int x,int y){return (1LL*x+y)%MOD;}
    int mul(int x,int y){return 1LL*x*y%MOD;}
    int ksm(int x,int y){
    	int ret=1,base=x;
    	for (;y;y>>=1,base=mul(base,base))
    		if (y&1) ret=mul(ret,base);
    	return ret;
    }
    void prework(int n){
    	f[0][0]=1;
    	for (int i=1;i<=n;++i){
    		for (int j=i/2;j<=i;++j)
    			f[i][j]=plu(mul(2,f[i-1][j-1]),f[i-2][j-1]);
    	}
    	for (int i=0;i<N;++i)
    		inv[i]=ksm(i,MOD-2);
    }
    void dp(){
    	int tmp1=1,tmp2,ttmp,pw;
    	for (int i=0;i<=K;++i){
    		tmp2=1; ttmp=(K-i+1)/2; pw=1;
    		for (int j=0;j<ttmp;++j) tmp2=mul(tmp2,mul(m-j,inv[j+1]));
    		pw=ksm(2,m-(K-i+1)/2);
    		for (int j=ttmp;j<=K-i;++j){
    			ans=plu(ans,mul(tmp1,mul(tmp2,mul(pw,f[K-i][j]))));
    			tmp2=mul(tmp2,mul(m-j,inv[j+1]));
    			pw=mul(pw,inv[2]);
    		}
    		tmp1=mul(tmp1,mul(n-m-i,inv[i+1]));
    	}
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d",&T);
    	prework(300);
    	for (int o=1;o<=T;++o){
    		scanf("%d%d%d",&n,&m,&K);
    		ans=0;
    		dp();
    		printf("%d
    ",ans);
    	}
    }
    
  • 相关阅读:
    matlab cell
    matlab linux 快捷键设置——有问题还是要解决
    latex 小结
    TOJ 1258 Very Simple Counting
    TOJ 2888 Pearls
    HDU 1248 寒冰王座
    TOJ 3486 Divisibility
    TOJ 3635 过山车
    TOJ 1840 Jack Straws
    HDU 4460 Friend Chains
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9720725.html
Copyright © 2011-2022 走看看