zoukankan      html  css  js  c++  java
  • 洛谷 U140604 送花

    洛谷 U140604 送花

    洛谷传送门

    题目背景

    SeawaySeawa**y的老婆大人RoseRos**e要过生日啦!为了给老婆大人庆生,SeawaySeawa**y正打算给RoseRos**e送上一束盛开着的鲜花......

    题目描述

    认真起来的SeawaySeawa**y为了庄重地庆贺这个生日,大手一挥,买下了NN个花箱。在编号为ii的箱子里,有f[i]f[i]朵相同颜色的花。每个箱子里面的花的颜色两两不同。SeawaySeawa**y要从这些箱子里挑选SS朵花扎成花束,送给老婆大人。SeawaySeawa**y当然可以随意挑选SS朵花。但因为他一共买下了这么多花,如果只组一种花束未免浪费。但RoseRos**e也不喜欢一次性收到太多的花束。所以SeawaySeawa**y便开始思考:对于他买下的这些花,他有多少种挑选方式,使之能够组成SS朵花的花束?SeawaySeawa**y规定,两种挑选方式不同,当且仅当存在同一种颜色的花数量不同。由于答案可能很大,请输出答案对10^9+7109+7取模的结果。

    输入格式

    从文件blossom.inblossom.i**n中读入数据。

    第一行两个整数N,SN,S,意义如题目描述所示。

    第二行NN个整数,第ii个整数表示第ii个花箱里的花的数量f[i]f[i]。

    输出格式

    输出到文件blossom.outblossom.out中。

    输出一行一个整数ansans,表示总挑选方案数量按要求取模后的结果。


    题解:

    题解:

    排列组合这个地方的确比较难理解,来给大家用大白话讲一下。

    多重集排列组合。

    不妨反着来:对于有(N)种元素的多重集(S),选(K)个元素,注意是个不是种,的可行方案数。可以变成:现在有(N)个篮子,把(K)个元素扔进这些篮子里的方案数。

    注意,这种是特殊情况,也就是说,每种元素无限多个可供挑选。

    这样的话,用隔板法解决问题。

    容易得出,答案也就是(C_{N+K-1}^{N-1})

    解释一下,现在有(K)个元素,分成(N)堆,也就是要往里插入(N-1)块板。按理讲应该是(C_{K+1}^{N-1}),但是因为允许有空集,也就是不插,那么就相当于每块板子插进去之后又产生了新元素,所以是这个答案。

    那么,根据多重集的限制,现在每种元素有一个数量上限,怎么办呢?

    很简单,采用容斥原理。关于容斥原理,请见:

    浅谈容斥原理

    上限是“至多放(f[i])个”,那么如果我往这个里面放(f[i]+1)个,是不是就不合法了?

    把不合法的减去即可。

    因为这道题N只有20,所以考虑状压,表示当前这个集合超没超。

    最后根据容斥,如果当前状态1的个数是奇数,就要减去,是偶数,就要加上。

    代码:

    #include<cstdio>
    #define ll long long
    using namespace std;
    const int maxn=25;
    const int mod=1e9+7;
    int n;
    ll s;
    ll f[maxn],inv[maxn];
    ll calc(ll n,ll m)
    {
    	if(n<0||m<0||n<m)
    		return 0;
    	n%=mod;
    	if(n==0||m==0)
    		return 1;
    	ll ret=1;
    	for(int i=0;i<m;i++)
    		ret=(ret*(n-i))%mod;
    	for(int i=1;i<=m;i++)
    		ret=(ret*inv[i])%mod;
    	return ret;
    }//n中选m的组合数
    ll qpow(ll a,ll b)
    {
    	ll ret=1;
    	while(b)
    	{
    		if(b&1)
    			ret=(ret*a)%mod;
    		a=(a*a)%mod;
    		b>>=1;
    	}
    	return ret;
    }
    int main()
    {
    	scanf("%d%lld",&n,&s);
    	for(int i=1;i<=n;i++)
    		scanf("%lld",&f[i]);
    	for(int i=1;i<=20;i++)
    		inv[i]=qpow(i,mod-2);
    	ll ans=calc(n+s-1,n-1)%mod;
    	for(int i=1;i<(1<<n);i++)
    	{
    		ll tot=n+s;
    		int p=0;
    		for(int j=0;j<n;j++)
    			if(i>>j&1)
    			{
    				p++;
    				tot-=(f[j+1]+1);
    			}
    		tot--;
    		if(p&1)
    			ans=(ans-calc(tot,n-1))%mod;
    		else
    			ans=(ans+calc(tot,n-1))%mod;
    	}
    	printf("%lld
    ",(ans+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    ubuntu(linux)虚拟主机部署桌面,使用window链接
    扫描shader
    Android Studio快捷键
    eclipse取消空格、等号、分号自动录入
    Libgdx学习记录28——创建Desktop程序
    设计模式19——代理模式
    设计模式18——模板方法
    设计模式17——解释器模式
    设计模式16——工厂模式
    设计模式15——外观模式
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13995977.html
Copyright © 2011-2022 走看看