zoukankan      html  css  js  c++  java
  • JZOJ3704 自古枪兵幸运E 题解

    题面

    -> Description
    俗话说,自古枪兵幸运E。而身为幸运E 的L 叔最想要的,就是C 妈的金羊毛了。然而这显然不是那么容易达成的。为了防止入侵者,C 妈花费了k 单位魔力,建造了包含了若干个消耗一单位魔力的小型防御设施和消耗两单位魔力的
    大型防御设施。
    经过很长时间的调查,L 叔终于了解到了C 妈可能采用的小型防御设施有n种,大型防御设施有m 种。每一种都可以建造任意个数个。由于L 叔拥有Rune文字的加持和B 级别的对魔力,只要了解每一种防御设施的数量,就可以破解
    这样的防御。
    然而,因为L 叔的幸运是E,所以他只有在尝试每一种方案之后才能找到破解的方法。他找到你,想让你判断一下他要尝试多少次才找到破解方法。由于凯尔特神话中对数字的迷信,你只要输出答案模p 的值就可以了。

    -> Input
    第一行一个正整数t,表示数据组数。
    接下来t 行每行四个非负整数n, m, k, p。

    -> Output
    t 行每行一个整数,表示答案模p 的值。

    -> Sample Input
    3
    0 10 2 47
    2 2 4 47
    5 5 10 47

    -> Sample Output
    10
    14
    6

    -> Data Constraint
    对于30%的数据,k ≤ 10^5。
    对于60%的数据,n, m ≤ 10^3,p ≤ 2 × 10^4。
    对于100%的数据,t ≤ 20,n, m ≤ 10^5,k ≤ 10^12,p ≤ 10^6,p 为质数。

    题目大意

    有n种大小为1的物品,每种有无限个.
    有m种大小为2的物品,每种有无限个.
    求填满大小为k的背包的方案数mod P.

    思路

    对于30%的数据,直接做背包.60%的数据也差不多。
    对于100%的数据,k的大小显然是不能背包了,考虑构造生成函数来表示n种和m种物品。
    先考虑大小为1的物品,其有无限个,因此这类物品的生成函数就是:

    这类物品有n种,因此这n种的组合就是:

    同样的,第二类物品的生成函数为:

    m种组合起来就是:

    将两类物品组合就是:

    我们现在就是要求这个式子展开后x^k项的系数。但是两个式子都是无限项的多项式,枚举复杂度是O(k)的。我们考虑让这个式子变形一下,令其出现一个有限项的多项式:

    后面一项其实就是二项式的n次方,可以展开成n+1项,于是我们就可以枚举后面一项展开后的每一项xi,找到前面一项里x(k-i)的系数,将他们的系数相乘就是答案,计算组合数要用到Lucas定理(k很大)。
    前面一项的展开需要使用广义二项式定理:

    于是:

    也就是这个多项式中x的指数只可能是偶数,我们就要在枚举里跳过掉(k-i)不是偶数的情况。
    代码:

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    
    typedef long long ll;
    const ll P = 1e6 + 7;
    int T;
    ll n, m, k, p, ans, fac[P], inv[P];
    
    ll C(ll n, ll m)
    {
    	if (n < m) return 0;
    	return fac[n] * inv[m] % p * inv[n - m] % p;
    }
    ll Lucas(ll n, ll m)
    {
    	if (n < p && m < p) return C(n, m);
    	return Lucas(n % p, m % p) * Lucas(n / p, m / p);
    }
    
    int main()
    {
    	freopen("luckye.in", "r", stdin);
    	freopen("luckye.out", "w", stdout);
    
    	scanf("%d", &T);
    	fac[0] = inv[0] = inv[1] = 1;
    	while (T--)
    	{
    		scanf("%lld%lld%lld%lld", &n, &m, &k, &p);
    		for (int i = 2; i <= p; i++) inv[i] = (p - p / i) * inv[p % i] % p;
    		for (int i = 2; i <= p; i++) inv[i] = inv[i] * inv[i - 1] % p; //阶乘的逆元
    		for (int i = 1; i <= p; i++) fac[i] = fac[i - 1] * i % p;
    		ans = 0;
    		for (int i = 0, x; i <= n; i++)
    		{
    			if ((k - i) & 1) continue;
    			ans = (ans + Lucas(n, i) * Lucas(n + m + ((k - i) / 2) - 1, n + m - 1) % p) % p;
    		}
    		printf("%lld
    ", ans);
    	}
    
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    pip下载速度慢&如何使用国内源提高速度
    pip更新安装删除包
    如何让VSCode同时打开(显示)多个项目
    JavaScript计算器
    在Ubuntu下搭建Android开发环境(AndroidStudio)
    在Windows中安装vim
    硬盘分区教程
    如何在Windows系统下使用you-get下载网上的媒体资源
    mencoder及ffmpeg的基本命令
    笔记本如何不按Fn键就能实现F键的功能
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/9278434.html
Copyright © 2011-2022 走看看