zoukankan      html  css  js  c++  java
  • [提高组集训2021] 教数学的校长

    一、题目

    无聊的校长 ( t DDXYX) 在写一些数列,他想出来一个问题想难倒你。

    对于两个长度为 (k) 的数列 ({a},{b}),满足 (sum_{i=1}^ka_i=n,sum_{i=1}^kb_i=m)

    对于这两个数列定义权值 (P=prod_{i=1}^kmin(a_i,b_i)),求 (sum_{{a},{b}}P)(998244353) 取模的结果。

    (n,m,kleq 5cdot 10^5)

    二、解法

    校长的做法

    校长觉得我做不出来这么难的题,所以就把他绝妙的做法告诉我了~

    寻找答案的组合意义,我们可以新定义一个数组 (c) 满足 (0leq c_i<min(a_i,b_i))

    现在变成了一个嵌套计数问题,也即对于所有的 (a,b) 序列求出对应 (c) 序列的个数,贡献法考虑每个 (c) 序列贡献给了多少 (a,b) 序列,我们枚举 (c) 序列的总和 (i)(a,b) 序列看成在原有的 (c) 序列上增加

    [sum P=sum_{i=0}{n-i-1choose k-1}{m-i-1choose k-1}{i+k-1choose k-1} ]

    校长牛逼!

    构造版生成函数

    如果从生成函数的角度考虑就是那个 (min(a_i,b_i)) 比较鬼畜,写方案数的生成函数还是比较容易的:

    [[x^ny^m][(x^1+x^2...)(y^1+y^2...)]^k ]

    现在我们要让 (x^ay^b) 前面的系数是 (min(a,b)),因为我们尽量要用基本的生成函数表示,所以我们把它拆分成 (x^0y^0cdot x^ay^b+x^1y^1cdot x^{a-1}y^{b-1}...+x^{min(a,b)-1}y^{min(a,b)-1}cdot x^1y^1),那么我们乘一个 ((x^0y^0+x^1y^1...)) 上去即可:

    [[x^ny^m][(x^1+x^2...)(y^1+y^2...)(x^0y^0+x^1y^1...)]^k ]

    那么我们枚举 (x^iy^i)(i),然后剩下所有的方案数都可以用隔板法计算,就得到和校长方法一样的柿子了。

    三、总结

    组合意义一定要流畅,一开始我想的是放球,有鸡巴的意义

    快速计算生成函数可以考虑拆分成基本形式,这时候就有点算贡献的思想了。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int MOD = 998244353;
    const int N = 500005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,m,k,ans,fac[N],inv[N];
    void init(int n)
    {
    	fac[0]=inv[0]=inv[1]=1;
    	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<=n;i++) inv[i]=inv[i-1]*inv[i]%MOD;
    	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD; 
    }
    int C(int n,int m)
    {
    	if(n<m || m<0) return 0;
    	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
    }
    void work()
    {
    	n=read();m=read();k=read();
    	int lim=min(n,m)-k;ans=0;
    	for(int i=0;i<=lim;i++)
    		ans=(ans+C(n-i-1,k-1)*C(m-i-1,k-1)%MOD*C(i+k-1,k-1))%MOD;
    	printf("%lld
    ",ans);
    }
    signed main()
    {
    	freopen("easy.in","r",stdin);
    	freopen("easy.out","w",stdout);
    	T=read();init(5e5);
    	while(T--) work();
    }
    
  • 相关阅读:
    Linux 下安装JDK1.8
    INSERT IGNORE 与INSERT INTO的区别
    linux安装redis 完整步骤
    从0开始 图论学习 广度优先搜索 链式前向星表示法
    从0开始 图论学习 深度优先遍历 链式前向星表示法
    从0开始 图论学习 链式前向星 最好的建图方法
    从0开始 图论学习 邻接表 STL vector
    从0开始 图论学习 前向星表示法
    数据结构实习
    数据结构实习
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15238558.html
Copyright © 2011-2022 走看看