2021牛客暑期多校训练营9 C、Cells

2024-02-19 22:48

本文主要是介绍2021牛客暑期多校训练营9 C、Cells,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C、Cells

参考过的题解

题目大意

你有一张无穷大的二维矩阵,你的出发点在格点 ( 0 , a i ) (0,a_i) (0,ai),并且保证了出发点横坐标依次递增: a i < a i + 1 a_i<a_{i+1} ai<ai+1

你的目标点分别是 ( 1 , 0 ) , ( 2 , 0 ) . . . ( n , 0 ) (1,0),(2,0)...(n,0) (1,0),(2,0)...(n,0),你有几个出发点就有几个目标点,求从出发点去目标点走过的路径没有任何交点的方案数。

Solution

考点:LGV引理+多项式卷积

这题第一个难点就是要会转换模型,他的方案数本质上就是一个LGV引理的模板。

稍微提一下LGV引理,它讲的就是你有起点 ( A 1 , A 2 . . . A n ) (A_1,A_2...A_n) (A1,A2...An),目标点 ( B 1 , B 2 . . . B n ) (B_1,B_2...B_n) (B1,B2...Bn),那么你从 n n n个起点出发经过完全不相交的边去到 n n n个终点的方案数会等于下面的行列式。

M = ∣ e ( A 1 , B 1 ) e ( A 1 , B 2 ) ⋯ e ( A 1 , B n ) e ( A 2 , B 1 ) e ( A 2 , B 2 ) ⋯ e ( A 2 , B n ) ⋮ ⋮ ⋱ ⋮ e ( A n , B 1 ) e ( A n , B 2 ) ⋯ e ( A n , B n ) ∣ M= \left| \begin{array}{cccc} e(A_1,B_1) & e(A_1,B_2) & \cdots & e(A_1,B_n) \\ e(A_2,B_1) & e(A_2,B_2) & \cdots & e(A_2,B_n) \\ \vdots & \vdots & \ddots & \vdots \\ e(A_n,B_1) & e(A_n,B_2) & \cdots & e(A_n,B_n) \\ \end{array} \right| M=e(A1,B1)e(A2,B1)e(An,B1)e(A1,B2)e(A2,B2)e(An,B2)e(A1,Bn)e(A2,Bn)e(An,Bn)

e ( A 1 , B 1 ) e(A_1,B_1) e(A1,B1)对应的是从 A 1 A_1 A1去往 B 1 B_1 B1的方案数,那么对应到本题,从 a 1 a_1 a1去往 ( 1 , 0 ) (1,0) (1,0)的方案数就是经典过河卒问题,它的方案数应该是 C a 1 + 1 1 C_{a_1+1}^1 Ca1+11

写出本题的行列式:

M = ∣ C a 1 + 1 1 C a 1 + 2 2 ⋯ C a 1 + n n C a 2 + 1 1 C a 2 + 2 2 ⋯ C a 2 + 2 n ⋮ ⋮ ⋱ ⋮ C a n + 1 1 C a n + 2 2 ⋯ C a n + n n ∣ M= \left| \begin{array}{cccc} C_{a_1+1}^{1} & C_{a_1+2}^{2} & \cdots & C_{a_1+n}^{n} \\ C_{a_2+1}^{1} & C_{a_2+2}^{2} & \cdots & C_{a_2+2}^{n} \\ \vdots & \vdots & \ddots & \vdots \\ C_{a_n+1}^{1} & C_{a_n+2}^{2} & \cdots & C_{a_n+n}^{n} \\ \end{array} \right| M=Ca1+11Ca2+11Can+11Ca1+22Ca2+22Can+22Ca1+nnCa2+2nCan+nn

求解 n n n阶方阵的常用做法是高斯消元,但是很明显 O ( n 3 ) O(n^3) O(n3)的做法无法通过此题,我们要对这个有特点的行列式做一些初等变换。

我们把这个方阵里面的组合数用阶乘的方式打开,可以得到下面的行列式。

M = ∣ ( a 1 + 1 ) ! 1 ! a 1 ! ( a 1 + 2 ) ! 2 ! a 1 ! ⋯ ( a 1 + n ) ! n ! a 1 ! ( a 2 + 1 ) ! 1 ! a 2 ! ( a 2 + 2 ) ! 2 ! a 2 ! ⋯ ( a 2 + n ) ! n ! a 2 ! ⋮ ⋮ ⋱ ⋮ ( a n + 1 ) ! 1 ! a n ! ( a n + 2 ) ! 2 ! a n ! ⋯ ( a n + n ) ! n ! a n ! ∣ M= \left| \begin{array}{cccc} \frac{(a_1+1)!}{1!a_1!} & \frac{(a_1+2)!}{2!a_1!} & \cdots & \frac{(a_1+n)!}{n!a_1!} \\ \frac{(a_2+1)!}{1!a_2!} & \frac{(a_2+2)!}{2!a_2!} & \cdots & \frac{(a_2+n)!}{n!a_2!} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{(a_n+1)!}{1!a_n!} & \frac{(a_n+2)!}{2!a_n!} & \cdots & \frac{(a_n+n)!}{n!a_n!} \\ \end{array} \right| M=1!a1!(a1+1)!1!a2!(a2+1)!1!an!(an+1)!2!a1!(a1+2)!2!a2!(a2+2)!2!an!(an+2)!n!a1!(a1+n)!n!a2!(a2+n)!n!an!(an+n)!

我们发现第 i i i列都有 1 i ! \frac{1}{i!} i!1我们把他提出外面去得到:

M = ∏ i = 1 n 1 i ! ∣ ( a 1 + 1 ) ! a 1 ! ( a 1 + 2 ) ! a 1 ! ⋯ ( a 1 + n ) ! a 1 ! ( a 2 + 1 ) ! a 2 ! ( a 2 + 2 ) ! a 2 ! ⋯ ( a 2 + n ) ! a 2 ! ⋮ ⋮ ⋱ ⋮ ( a n + 1 ) ! a n ! ( a n + 2 ) ! a n ! ⋯ ( a n + n ) ! a n ! ∣ M=\prod\limits_{i=1}^n\frac{1}{i!} \left| \begin{array}{cccc} \frac{(a_1+1)!}{a_1!} & \frac{(a_1+2)!}{a_1!} & \cdots & \frac{(a_1+n)!}{a_1!} \\ \frac{(a_2+1)!}{a_2!} & \frac{(a_2+2)!}{a_2!} & \cdots & \frac{(a_2+n)!}{a_2!} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{(a_n+1)!}{a_n!} & \frac{(a_n+2)!}{a_n!} & \cdots & \frac{(a_n+n)!}{a_n!} \\ \end{array} \right| M=i=1ni!1a1!(a1+1)!a2!(a2+1)!an!(an+1)!a1!(a1+2)!a2!(a2+2)!an!(an+2)!a1!(a1+n)!a2!(a2+n)!an!(an+n)!

接下来就要作过一定线性代数的题了,要认出这是一个范德蒙德行列式。

具体我们也能推出来,我们假设取了第一行的前三列,我们可以得出下面的这个行列式。

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ∗ ( a 1 + 3 ) ∣ \left| \begin{array}{cccc} (a_1+1) & (a_1+1)*(a_1+2) & (a_1+1)*(a_1+2)*(a_1+3) \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)(a1+2)(a1+3)

为了好看我们转置一下,并且要知道行列式转置值不变,我们得出下面的这个三行一列行列式,本质上和上面的是一样的。

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ∗ ( a 1 + 3 ) ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)*(a_1+2)*(a_1+3) \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)(a1+2)(a1+3)

我们接下来做行初等变换,第二行乘以 − 2 -2 2加到第三行上去,得到

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) 2 ∗ ( a 1 + 2 ) ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)^2*(a_1+2) \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)2(a1+2)

接下来用第一行乘以 − ( a 1 + 1 ) -(a_1+1) (a1+1)加到第三行上去,这样我们就得到

∣ ( a 1 + 1 ) ( a 1 + 1 ) ∗ ( a 1 + 2 ) ( a 1 + 1 ) 3 ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)*(a_1+2) \\ (a_1+1)^3 \end{array} \right| (a1+1)(a1+1)(a1+2)(a1+1)3

接着用第一行乘以 − 1 -1 1加到第二行计算到

∣ ( a 1 + 1 ) ( a 1 + 1 ) 2 ( a 1 + 1 ) 3 ∣ \left| \begin{array}{cccc} (a_1+1) \\ (a_1+1)^2 \\ (a_1+1)^3 \end{array} \right| (a1+1)(a1+1)2(a1+1)3

这样一个范德蒙德行列式就出来了,我们再把转置之后的行列式每一列提出一个 ( a i + 1 ) (a_i+1) (ai+1)就变成了标准的范德蒙德行列式。

M = ∏ i = 1 n a i + 1 i ! ∣ 1 1 ⋯ 1 ( a 1 + 1 ) ( a 2 + 1 ) ⋯ ( a n + 1 ) ⋮ ⋮ ⋱ ⋮ ( a 1 + 1 ) n − 1 ( a 2 + 1 ) n − 1 ⋯ ( a n + 1 ) n − 1 ∣ = ∏ i = 1 n ( a i + 1 i ! ∏ 1 ≤ i < j ≤ n ( a j − a i ) ) M=\prod\limits_{i=1}^n\frac{a_i+1}{i!} \left| \begin{array}{cccc} 1 & 1 & \cdots & 1 \\ (a_1+1) & (a_2+1) & \cdots & (a_n+1) \\ \vdots & \vdots & \ddots & \vdots \\ (a_1+1)^{n-1} & (a_2+1)^{n-1} & \cdots & (a_n+1)^{n-1} \\ \end{array} \right| =\prod\limits_{i=1}^n(\frac{a_i+1}{i!}\prod\limits_{1\le i<j\le n}(a_j-a_i)) M=i=1ni!ai+11(a1+1)(a1+1)n11(a2+1)(a2+1)n11(an+1)(an+1)n1=i=1n(i!ai+11i<jn(ajai))

对于后面那个 ∏ 1 ≤ i < j ≤ n n a j − a i \prod\limits_{1\le i<j\le n}^n a_j-a_i 1i<jnnajai,可以比较自然的想到用多项式乘法计算个数。

构造 f ( x ) = ∑ x − a i , g ( x ) = ∑ x a j f(x)=\sum x^{-a_i},g(x)=\sum x^{a_j} f(x)=xai,g(x)=xaj

那么我们把 f ( x ) ∗ g ( x ) f(x)*g(x) f(x)g(x)卷积之后得到的 F ( x ) = ∑ c i ∗ x i F(x)=\sum c_i*x^i F(x)=cixi,它的每个指数 i i i对应着一个 a j − a i a_j-a_i ajai,它的系数就是这个差值出现的次数,由于本题要求的都是 ∏ \prod 符号,所以我们预处理前面的阶乘和分子,后面的我们枚举全部的大于 0 0 0的幂次,做快速幂就可以实现了,还有要注意的两点,第一是实现的时候 N T T NTT NTT不能用负数做下标,所以我们要用一个偏移量 P = 1000001 P=1000001 P=1000001去处理一下负数,第二本身由于题目给了 a i < a i + 1 a_i<a_{i+1} ai<ai+1所以对于相同的 x = a j − a i x=a_j-a_i x=ajai来说最多就是 n n n个,不会超过 5 ⋅ 1 0 5 5·10^5 5105个,也不用考虑其他的欧拉降幂去求解快速幂。

综上这题的时间复杂度就在 O ( n log ⁡ n ) O(n\log n) O(nlogn)时间内愉快的搞定了,挺好的一道LGV引理题和数学题。

int n;
const int P = 1000001;
int a[500005], jc[500005], inv[500005];namespace math {const int MOD = 998244353;inline int add(int x, const int y) { return x += y, x >= MOD ? x - MOD : x; }inline int sub(int x, const int y) { return x -= y, x < 0 ? x += MOD : x; }inline int mul(const int x, const int y) { return 1ll * x * y % MOD; }inline int qpow(int x, int y) {int res = 1;for (; y; y >>= 1, x = mul(x, x)) if (y & 1) res = mul(res, x);return res;}
}using namespace math;namespace NTT {int limit;int pr = 3; // 能用的ntt的素数原根vector<int> A, B, rev;void init() {int ed = P << 1, bit = -1;for (limit = 1; limit <= ed; limit <<= 1) ++bit;A.resize(limit); B.resize(limit); rev.resize(limit);for (int i = 0; i < limit; ++i)	rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit);}void ntt(vector<int>& P, int op) {for (int i = 0; i < limit; ++i) {if (i < rev[i])swap(P[i], P[rev[i]]);}for (int mid = 1; mid < limit; mid <<= 1) {int euler = qpow(pr, (MOD - 1) / (mid << 1));if (op < 0)	euler = qpow(euler, MOD - 2);for (int i = 0, pos = mid << 1; i < limit; i += pos) {int wk = 1;for (int j = 0; j < mid; ++j, wk = mul(wk, euler)) {int x = P[i + j], y = mul(wk, P[i + j + mid]);P[i + j] = add(x, y), P[i + j + mid] = add(x, MOD - y);}}}if (op > 0)	return;int inv = qpow(limit, MOD - 2);for (int i = 0; i < limit; ++i)	P[i] = mul(P[i], inv);}void work() {for (int i = 1; i <= n; ++i) A[a[i]] = 1;for (int i = 1; i <= n; ++i) B[P - a[i]] = 1;ntt(A, 1), ntt(B, 1);for (int i = 0; i < limit; ++i)	A[i] = mul(A[i], B[i]);ntt(A, -1);}
};int solve() {n = read();jc[0] = 1;for (int i = 1; i <= n; ++i) {a[i] = read();jc[i] = jc[i - 1] * i % MOD;}inv[n] = qpow(jc[n], MOD - 2);for (int i = n - 1; ~i; --i) {inv[i] = inv[i + 1] * (i + 1) % MOD;}int ans = 1;for (int i = 1; i <= n; ++i) {ans = mul(mul(ans, (a[i] + 1)), inv[i]);}NTT::init();NTT::work();for (int i = P + 1; i <= 2 * P; ++i) {ans = mul(ans, qpow(i - P, NTT::A[i]));}print(ans);return 1;
}

这篇关于2021牛客暑期多校训练营9 C、Cells的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/726240

相关文章

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

暑期学习总结

iOS学习 前言无限轮播图换头像网络请求按钮的configuration属性总结 前言 经过暑期培训,完成了五个项目的仿写,在项目中将零散的内容经过实践学习,有了不少收获,因此来总结一下比较重要的内容。 无限轮播图 这是写项目的第一个难点,在很多项目中都有使用,越写越熟练。 原理为制造两个假页,在首和尾分别制作最后一页和第一页的假页,当移动到假页时,使用取消动画的方式跳到

GPU 计算 CMPS224 2021 学习笔记 02

并行类型 (1)任务并行 (2)数据并行 CPU & GPU CPU和GPU拥有相互独立的内存空间,需要在两者之间相互传输数据。 (1)分配GPU内存 (2)将CPU上的数据复制到GPU上 (3)在GPU上对数据进行计算操作 (4)将计算结果从GPU复制到CPU上 (5)释放GPU内存 CUDA内存管理API (1)分配内存 cudaErro

2021-8-14 react笔记-2 创建组件 基本用法

1、目录解析 public中的index.html为入口文件 src目录中文件很乱,先整理文件夹。 新建components 放组件 新建assets放资源   ->/images      ->/css 把乱的文件放进去  修改App.js 根组件和index.js入口文件中的引入路径 2、新建组件 在components文件夹中新建[Name].js文件 //组件名首字母大写

2021-08-14 react笔记-1 安装、环境搭建、创建项目

1、环境 1、安装nodejs 2.安装react脚手架工具 //  cnpm install -g create-react-app 全局安装 2、创建项目 create-react-app [项目名称] 3、运行项目 npm strat  //cd到项目文件夹    进入这个页面  代表运行成功  4、打包 npm run build

代码训练营 Day26 | 47.排序II | 51. N-皇后 |

47.排序II 1.跟46题一样只不过加一个树层去重 class Solution(object):def backtracking(self,nums,path,result,used):# recursion stopif len(path) == len(nums):# collect our setresult.append(path[:])return for i in range(

代码随想录训练营day37|52. 携带研究材料,518.零钱兑换II,377. 组合总和 Ⅳ,70. 爬楼梯

52. 携带研究材料 这是一个完全背包问题,就是每个物品可以无限放。 在一维滚动数组的时候规定了遍历顺序是要从后往前的,就是因为不能多次放物体。 所以这里能多次放物体只需要把遍历顺序改改就好了 # include<iostream># include<vector>using namespace std;int main(){int n,m;cin>>n>>m;std::vector<i

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

牛客小白月赛100(A,B,C,D,E,F三元环计数)

比赛链接 官方讲解 这场比较简单,ABC都很签到,D是个不太裸需要预处理的 B F S BFS BFS 搜索,E是调和级数暴力枚举,F是三元环计数。三元环考的比较少,没见过可能会偏难。 A ACM中的A题 思路: 就是枚举每个边变成原来的两倍,然后看看两短边之和是否大于第三边即可。 不能只给最短边乘 2 2 2,比如 1 4 8 这组数据,也不能只给第二短边乘 2 2 2,比

代码随想录算法训练营Day37|完全背包问题、518.零钱兑换II、377. 组合总和 Ⅳ、70. 爬楼梯(进阶版)

完全背包问题                  和01背包最大区别就是一个物品可以重复放多次,因此遍历空间时可以从前往后。 import java.util.*;public class Main{public static void main (String[] args) {Scanner sc = new Scanner(System.in);int m = sc.nextInt