【CTS2019】珍珠(生成函数)
【CTS2019】珍珠(生成函数)
题面
题解
lun题可海星。
首先一个大暴力\(sb\)的\(dp\)是设\(f[i][S]\)表示当前考虑完了前\(i\)个珍珠,\(S\)集合中这些颜色的珍珠当前还有一个没有匹配。这个随便转移就行了。
然后发现并没有任何需要记录下确切的哪些颜色是奇数个,只需要记录有多少种就行了。
这样子可以做到\(O(nd)\)。
从这里我们看出,最终能够匹配出来的对数恰好等于\((n-|S|)/2\),总个数减去奇数颜色的个数的一半。
首先如果我们能够知道每种颜色的奇偶情况,那么每个颜色都可以写成一个指数型生成函数,然后全部乘起来的第\(n\)项就是答案。
其中,奇数的生成函数是:\(A(x)=\frac{e^x-e^{-x}}{2}\),偶数的生成函数是:\(B(x)=\frac{e^x+e^{-x}}{2}\)。
那么我们假设枚举出来有\(p\)个颜色是奇数,方案数就是\([x^n]A^p(x)B^{d-p}(x)\)。
所以答案式:
ans&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n]{d\choose p}(e^x-e^{-x})^p(e^x+e^{-x})^P\\
&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n]{d\choose p}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}{p\choose i}{d-p\choose j}e^{(2i+2j-d)x}\\
&=\frac{1}{2^d}\sum_{p=0}^{n-2m}{d\choose p}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}{p\choose i}{d-p\choose j}(2i+2j-d)^n\\
&=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}\frac{(-1)^{p-i}(2i+2j-d)^n}{i!(p-i)!j!(d-p-j)!}
\end{aligned}\]
这样子直接暴力可以做到\(O(d^3)\)。
考虑接着往下进行一些变化,首先我们发现我们的这个式子麻烦在\(i,j\)既有分开的项,又有\(i+j\)的项,那么把这两个部分给分开看。
ans&=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}\frac{(-1)^{p-i}(2i+2j-d)^n}{i!(p-i)!j!(d-p-j)!}\\
&=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}(2(i+j)-d)^n\frac{1}{i!j!}\frac{1}{(p-i)!(d-p-j)!}\\
&=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}\frac{(2(i+j)-d)^n}{(i+j)!(d-(i+j))!}\frac{(i+j)!}{i!j!}\frac{(d-(i+j))!}{(p-i)!(d-p-j)!}\\
&=\frac{1}{2^d}d!\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}\frac{(2(i+j)-d)^n}{(i+j)!(d-(i+j))!}{i+j\choose i}{d-(i+j)\choose p-i}\\
&=\frac{1}{2^d}\sum_{p=0}^{n-2m}\sum_{i=0}^p\sum_{j=0}^{d-p}(-1)^{p-i}(2(i+j)-d)^n{d\choose i+j}{i+j\choose i}{d-(i+j)\choose p-i}\\
\end{aligned}\]
这样子再往下我似乎就不会推了,难受。
\(memset0\)的做法可以在洛谷的题解内看到,我这里大致的复述一下。
考虑把后面的组合数转变为格路问题,\(i+j\choose i\)是从\((0,0)\)走到\((i,j)\)的方案数,\(d-(i+j)\choose p-i\)是从\((i,j)\)走到\((p,d-p)\)的方案数。
然后这里还有一个\((-1)^{p-i}\)的贡献,格路这个东西也可以写成一个生成函数,既然作为杨辉三角的一层,所以生成函数就是\((1+x)^d\)的形式。
然后这里枚举的时候并没有单独出现过\(j\)了,所以只需要枚举\(i+j\)会更加方便。
ans&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}\sum_{p=0}^{n-2m}[x^p](1+x)^i(1-x)^{d-i}
\end{aligned}\]
讲讲这个为什么是对的,首先\((-1)^{p-i}\)表示只和第二次走的距离相关,而横坐标总共要走\(p\)步,第一次走不会产生符号,所以就是\((1+x)^i\),第二次会产生符号,之和走的横坐标的步数相关,奇数步是\(-1\),偶数步是\(1\),所以是\((1-x)^{d-i}\)。
那么令\(F(i,d)=\sum_{p=0}^{n-2m}[x^p](1+x)^i(1-x)^{d-i}\)
所以有
ans&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}\sum_{p=0}^{n-2m}[x^p](1+x)^i(1-x)^{d-i}\\
&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}F(i,d)
\end{aligned}\]
考虑怎么计算\(F\),那么考虑能不能递推。
首先边界\(F(0,0)=1\)。
F(0,d)&=\sum_{k=0}^{n-2m}[x^k](1-x)^d\\
&=\sum_{k=0}^{n-2m}{d\choose k}(-1)^k\\
&=\sum_{k=0}^{n-2m}(-1)^k({d-1\choose k}+{d-1\choose k-1})\\
&=\sum_{k=0}^{n-2m}(-1)^k{d-1\choose k}+\sum_{k=0}^{n-2m-1}(-1)^{k+1}{d-1\choose k}\\
&=(-1)^{n-2m}{d-1\choose n-2m}
\end{aligned}\]
然后考虑其他的情况,
F(i,d)&=\sum_{k=0}^{n-2m}[x^k](1-x)^{d-i}(1+x)^i\\
&=\sum_{k=0}^{n-2m}[x^k](1+x)(1+x)^{i-1}(1-x)^{d-i}\\
&=\sum_{k=0}^{n-2m}[x^k](-(1-x)+2)(1+x)^{i-1}(1-x)^{d-i}\\
&=-\sum_{k=0}^{n-2m}[x^k](1+x)^{i-1}(1-x)^{d-i+1}+2\sum_{k=0}^{n-2m}[x^k](1+x)^{i-1}(1-x)^{d-i}\\
&=-F(i-1,d)+2F(i-1,d-1)
\end{aligned}\]
于是这个东西肉眼可见的可以继续在网格图上走,即有两种走法,一种是沿着横坐标走一步,另外一种是两个坐标一起走一步。
发现无论如何横坐标都要走,所以唯一需要决策的只有竖坐标的移动,而两种移动方式分别产生\(-1\)和\(2\)的贡献,那么我们枚举起点就可以知道竖坐标要走多少步,然后组合数就可以随便算了。
所以得到:
\]
这样子愉快的把答案式给展开:
ans&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}F(i,d)\\
&=\frac{1}{2^d}\sum_{i=0}^d(2i-d)^n{d\choose i}\sum_{j=0}^d F(0,j){i\choose d-j}(-1)^{i+j-d}2^{d-j}\\
&=\frac{d!}{2^d}\sum_{i=0}^d\sum_{j=0}^d \frac{(-1)^{d-i-j}}{(i+j-d)!}\frac{(2i-d)^n}{(d-i)!}\frac{F(0,j)2^{d-j}}{(d-j)!}
\end{aligned}\]
后面两个东西可以处理成一个卷积,而前面那一项恰好只和\(i+j\)相关,那么直接\(NTT\)乘起来然后枚举一下就能算了。
然而这样子的推导很麻烦,lun课件中的方法是一致的,
但是中间推导的部分更加简单。
不一样的地方在于
\]
原本这里把奇偶分开然后统一的计算\([x^n]\),那么现在写成这样子:
\]
这样自己就一个式子钦定了奇数被选的次数。
也就是:
ans&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n][y^p](y(e^x-e^{-x})+(e^x+e^{-x}))^d\\
&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}[x^n][y^p](e^x(1+y)+e^{-x}(1-y))^d\\
&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}\sum_{i=0}^d e^{(2i-d)x}(1+y)^i(1-y)^{d-i}[x^n][y^p]\\
&=\frac{n!}{2^d}\sum_{p=0}^{n-2m}(2i-d)^n \sum_{i=0}^d((1+y)^i(1-y)^{d-i})[y^p]
\end{aligned}\]
然后令\(F(i,d)\)为后面那个\(\sum\),就和前面的推导一致了。
但是这样子方便多了,不需要再转化成格路来进行推导。
然后还有\(1t5t\)的方法,
我们只考虑枚举奇数至少有多少个,这样子我们枚举完奇数个数之后剩下的随意,也就是\(e^{(d-p)x}\)。
那么设\(f_i\)表示强制有\(i\)个为奇数的方案数,
有:
f_i&=[x^n]{d\choose i}(\frac{e^x-e^{-x}}{2})^ie^{(d-i)x}\\
&=[x^n]\frac{1}{2^i}\frac{d!}{i!(d-i)!}e^{(d-i)x}\sum_{j=0}^i {i\choose j}(-1)^j e^{(i-2j)x}\\
&=[x^n]\frac{1}{2^i}\frac{d!}{i!(d-i)!}\sum_{j=0}^i {i\choose j}(-1)^j e^{(d-2j)x}\\
&=\frac{1}{2^i}\frac{d!}{i!(d-i)!}\sum_{j=0}^i \frac{i!}{j!(i-j)!}(-1)^j (d-2j)^n
\end{aligned}\]
这样子就可以卷积算出所有的\(f\),然后用二项式反演去求解所有的恰好然后计算答案了。
代码是前两种做法的。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 550550
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int W[MAX],P[MAX],r[MAX];
void NTT(int *P,int len,int opt)
{
int l=0,N;for(N=1;N<len;N<<=1)++l;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
{
int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
for(int j=0,p=i<<1;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
}
}
if(opt==-1)
{
reverse(&P[1],&P[N]);
for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
}
}
int jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int A[MAX],B[MAX],F[MAX];
int D,n,m,ans;
int main()
{
D=read();n=read();m=read();
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=2;i<=D;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=D;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=1;i<=D;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
for(int i=0;i<=D;++i)A[i]=1ll*fpow(((i+i-D)%MOD+MOD)%MOD,n)*jv[D-i]%MOD;
for(int i=1;i<=D;++i)F[i]=1ll*((n&1)?MOD-1:1)%MOD*C(i-1,n-2*m)%MOD;F[0]=1;
for(int i=0;i<=D;++i)B[i]=1ll*fpow(2,D-i)*jv[D-i]%MOD*F[i]%MOD;
int N;for(N=1;N<=D+D;N<<=1);
NTT(A,N,1);NTT(B,N,1);
for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
NTT(A,N,-1);
for(int i=D;i<=D+D;++i)ans=(ans+1ll*(((D+i)&1)?MOD-1:1)*jv[i-D]%MOD*A[i])%MOD;
ans=1ll*ans*jc[D]%MOD*fpow(fpow(2,D),MOD-2)%MOD;
printf("%d\n",ans);
return 0;
}
【CTS2019】珍珠(生成函数)的更多相关文章
- LOJ3120 CTS2019 珍珠 生成函数、二项式反演、NTT
传送门 题目大意:给出一个长度为\(n\)的序列\(a_i\),序列中每一个数可以取\(1\)到\(D\)中的所有数.问共有多少个序列满足:设\(p_i\)表示第\(i\)个数在序列中出现的次数,\( ...
- [CTS2019]珍珠——二项式反演
[CTS2019]珍珠 考虑实际上,统计多少种染色方案,使得出现次数为奇数的颜色数<=n-2*m 其实看起来很像生成函数了 n很大?感觉生成函数会比较整齐,考虑生成函数能否把n放到数值的位置,而 ...
- 题解-CTS2019 珍珠
题面 CTS2019 珍珠 有 \(n\) 个在 \([1,d]\) 内的整数,求使可以拿出 \(2m\) 个整数凑成 \(m\) 个相等的整数对的方案数. 数据范围:\(0\le m\le 10^9 ...
- 【题解】CTS2019珍珠(二项式反演+卷积)
[题解]CTS2019珍珠 题目就是要满足这样一个条件\(c_i\)代表出现次数 \[ \sum {[\dfrac {c_i } 2]} \ge 2m \] 显然\(\sum c_i=n\)所以,而且 ...
- [CTS2019]珍珠(NTT+生成函数+组合计数+容斥)
这题72分做法挺显然的(也是我VP的分): 对于n,D<=5000的数据,可以记录f[i][j]表示到第i次随机有j个数字未匹配的方案,直接O(nD)的DP转移即可. 对于D<=300的数 ...
- [LOJ#3120][Luogu5401][CTS2019]珍珠(容斥+生成函数)
https://www.luogu.org/blog/user50971/solution-p5401 #include<cstdio> #include<algorithm> ...
- Luogu5401 CTS2019珍珠(生成函数+容斥原理+NTT)
显然相当于求有不超过n-2m种颜色出现奇数次的方案数.由于相当于是对各种颜色选定出现次数后有序排列,可以考虑EGF. 容易构造出EGF(ex-e-x)/2=Σx2k+1/(2k+1)!,即表示该颜色只 ...
- 题解 P5401 [CTS2019]珍珠
蒟蒻语 这题太玄学了,蒟蒻写篇题解来让之后复习 = = 蒟蒻解 假设第 \(i\) 个颜色有 \(cnt_i\) 个珍珠. \(\sum\limits_{i=1}^{n} \left\lfloor\f ...
- 洛谷 P5401 - [CTS2019]珍珠(NTT+二项式反演)
题面传送门 一道多项式的 hot tea 首先考虑将题目的限制翻译成人话,我们记 \(c_i\) 为 \(i\) 的出现次数,那么题目的限制等价于 \(\sum\limits_{i=1}^D\lflo ...
- 没有过的题QAQ
持续更新...纪念一下我的高分暴力...(好丢人啊qwq) NOI2014 动物园 80pts 用倍增暴力跳nxt数组 #include<iostream> #include<cst ...
随机推荐
- RV64I基础整数指令集
RV64I是RV32I的超集,RV32I是RV64I的子集.RV64I包括RV32I的所有40条指令,另外增加了12条RV32I中没有的指令,还有三条移位指令(slli, srli,srai)也进行小 ...
- 英语Lignaloes沉香木LIGNALOES单词
中文名沉香木 外文名Lignaloes 国内分布两广以及云南和福建等地 国外分布印度尼西亚.马来西亚.新加坡 沉香木是珍贵的香料,被用作燃烧熏香.提取香料.加入酒中,或直接雕刻成装饰品.沉香木又名沉水 ...
- vue学习指南:第四篇(详细) - vue的 :class 和 :style
1. :class = “a” 说明 vue 中有个叫 a 的属性 这个标签的class 就是 a的值 2. :class = “{ active:isactive }” Active的存在取决于 i ...
- 软件设计师【软件工程:软件开发模型、XP极限编程十二最佳实践】
一.软件开发模型 二.XP极限编程十二最佳实践
- linux 中断底半部机制对比(任务队列,工作队列,软中断)--由linux RS485引出的血案【转】
转自:http://blog.chinaunix.net/uid-20768928-id-5077401.html 在LINUX RS485的使用过程中,由于各种原因,最后不得不使用中断底半部机制的方 ...
- Python语言基础06-字符串和常用数据结构
本文收录在Python从入门到精通系列文章系列 1. 使用字符串 第二次世界大战促使了现代电子计算机的诞生,最初计算机被应用于导弹弹道的计算,而在计算机诞生后的很多年时间里,计算机处理的信息基本上都是 ...
- docker学习12-docker快速搭建禅道环境
前言 bug管理系统是每个公司测试团队必备的,当去一个新的公司组建一个测试团队的时候,需快读搭建一套bug管理系统,使用docker搭建禅道就非常方便. 拉取镜像 先拉取zentao镜像,镜像地址ht ...
- Appium+python自动化(二)- 环境搭建—下(超详解)
简介 宏哥的人品还算说得过去,虽然很久没有搭建环境了,但是换了新电脑设备,一气呵成,将android的测试开发环境已经搭建准备完毕.上一篇android测试开发环境已经准备好, 那么接下来就是appi ...
- MyBatis 使用 foreach 批量插入
MyBatis 使用 foreach 批量插入 参考博文 老司机学习MyBatis之动态SQL使用foreach在MySQL中批量插入 使用MyBatis一次性插入多条数据时候可以使用 <for ...
- http协议里定义的四种常见数据的post方法
原文 https://blog.csdn.net/charlene0824/article/details/51199292 关于http协议里定义的四种常见数据的post方法,分别是: applic ...