LOJ2527 HAOI2018 染色 容斥、生成函数、多项式求逆
调了1h竟然是因为1004535809写成了998244353
“恰好有\(K\)种颜色出现了\(S\)次”的限制似乎并不容易达到,考虑容斥计算。
令\(c_j\)表示强制\(j\)种颜色恰好出现\(S\)次,其他颜色随意染的方案数。可以通过生成函数知道
\(\begin{align*} c_j &= \binom{m}{j} n! [x^n] (\frac{x^k}{k!})^j (\sum\limits_{i=0}^\infty \frac{x^i}{i!})^{m-j} \\ &= \binom{m}{j} n! [x^n] (\frac{x^k}{k!})^j e^{(m-j)x} \\ &= \binom{m}{j} n! [x^n] (\frac{x^k}{k!})^j \sum\limits_{i=0}^\infty \frac{x^i (m-j)^i}{i!} \\ &= \binom{m}{j}n! \frac{(m-j)^{n-jk}}{(k!)^j (n-jk)!} \end{align*}\)
显然是可以预处理阶乘之后\(O(1)\)计算的。注意不要漏掉了指数型生成函数前面要乘的\(n!\)。
又设\(h_j\)表示恰好有\(j\)种颜色出现了\(S\)次的方案总数。不难发现有一个反演:\(h_j = c_j - \sum\limits_{i=j+1}^{Max} h_{i}A(i,j)\),\(A(i,j)\)是一个与\(i,j\)相关的系数,表示\(h_i\)在\(c_j\)中的出现次数。
既然\(h_i\)中恰好有\(i\)种颜色出现了\(S\)次,那么对于任意一个对\(h_i\)产生贡献的状态,只要枚举到当前状态中\(i\)种恰好出现了\(S\)次的颜色构成的集合的任意一个大小为\(j\)的子集时都会对\(c_j\)产生\(1\)的贡献。所以\(A(i,j) = \binom{i}{j}\)
所以可以得到\(h_j = c_j - \sum\limits_{i=j+1}^{Max}h_i \binom{i}{j}\),两边同乘\(j!\)得到\(h_jj! = c_jj! - \sum\limits_{i=j+1}^{Max}\frac{h_ii!}{(i-j)!}\)
设多项式\(H = \sum\limits_{i=0}^{Max}h_ii!x^i , C = \sum\limits_{i=0}^{Max}c_ii!x^i\),记\(rev(H)\)为多项式\(H\)所有系数翻转过来之后的多项式,那么不难得到\(rev(H) = rev(C) - W * rev(H)\),其中\(W = \sum\limits_{i=1}^{Max} \frac{1}{i!}x^i\)。多项式求逆即可。
Update:不难发现\(W+1 = e^x\),所以求逆的结果是\(e^{-x}\),所以可以不必求逆直接把\(e^{-x}\)的系数代替求逆;实际上这个反演的过程是二项式反演的一个变体,可以通过二项式反演的方式进行NTT,实质一样。
#include<iostream>
#include<cstdio>
#include<random>
#include<cstring>
#include<algorithm>
//This code is written by Itst
using namespace std;
const int mod = 998244353;
inline int read(bool flg = 0){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c) && c != EOF){
if(c == '-')
f = 1;
c = getchar();
}
if(c == EOF)
exit(0);
while(isdigit(c)){
if(flg)
a = (a * 10ll + c - 48) % mod;
else
a = a * 10 + c - 48;
c = getchar();
}
if(flg) a += mod;
return f ? -a : a;
}
const int MAXN = (1 << 19) + 7 , MAXM = 1e7 + 7 , MOD = 1004535809;
#define PII pair < int , int >
#define st first
#define nd second
inline int poww(long long a , int b){
int times = 1;
while(b){
if(b & 1)
times = times * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return times;
}
namespace poly{
const int G = 3 , INV = (MOD + 1) / G;
int A[MAXN] , B[MAXN] , C[MAXN] , D[MAXN] , E[MAXN];
int a[MAXN] , b[MAXN] , c[MAXN] , d[MAXN];
int need , inv , dir[MAXN] , _inv[MAXN];
#define clear(x) memset(x , 0 , sizeof(int) * need)
void init(int len){
need = 1;
while(need < len)
need <<= 1;
inv = poww(need , MOD - 2);
for(int i = 1 ; i < need ; ++i)
dir[i] = (dir[i >> 1] >> 1) | (i & 1 ? need >> 1 : 0);
}
void init_inv(){
_inv[1] = 1;
for(int i = 2 ; i < MAXN ; ++i)
_inv[i] = MOD - 1ll * (MOD / i) * _inv[MOD % i] % MOD;
}
void NTT(int *arr , int type){
for(int i = 1 ; i < need ; ++i)
if(i < dir[i])
arr[i] ^= arr[dir[i]] ^= arr[i] ^= arr[dir[i]];
for(int i = 1 ; i < need ; i <<= 1){
int wn = poww(type == 1 ? G : INV , (MOD - 1) / i / 2);
for(int j = 0 ; j < need ; j += i << 1){
long long w = 1;
for(int k = 0 ; k < i ; ++k , w = w * wn % MOD){
int x = arr[j + k] , y = arr[i + j + k] * w % MOD;
arr[j + k] = x + y >= MOD ? x + y - MOD : x + y;
arr[i + j + k] = x < y ? x + MOD - y : x - y;
}
}
}
}
void mul(int *a , int *b){
NTT(a , 1);NTT(b , 1);
for(int i = 0 ; i < need ; ++i)
a[i] = 1ll * a[i] * b[i] % MOD;
NTT(a , -1);
}
void getInv(int *a , int *b , int len){
if(len == 1){
b[0] = poww(a[0] , MOD - 2);
return;
}
getInv(a , b , (len + 1) >> 1);
memcpy(A , a , sizeof(int) * len);
memcpy(B , b , sizeof(int) * len);
init(len * 3);
NTT(A , 1);NTT(B , 1);
for(int i = 0 ; i < need ; ++i)
A[i] = 1ll * A[i] * B[i] % MOD * B[i] % MOD;
NTT(A , -1);
for(int i = 0 ; i < len ; ++i)
b[i] = (2 * b[i] - 1ll * A[i] * inv % MOD + MOD) % MOD;
clear(A);clear(B);
}
}
using namespace poly;
int F[MAXN] , H[MAXN] , jc[MAXM] , Inv[MAXM] , W[MAXN];
int N , M , K , Len;
void init(){
jc[0] = 1;
for(int i = 1 ; i <= N || i <= M ; ++i)
jc[i] = 1ll * jc[i - 1] * i % MOD;
Inv[max(N , M)] = poww(jc[max(N , M)] , MOD - 2);
for(int i = max(N , M) - 1 ; i >= 0 ; --i)
Inv[i] = Inv[i + 1] * (i + 1ll) % MOD;
}
int binom(int b , int a){
return b < a ? 0 : 1ll * jc[b] * Inv[a] % MOD * Inv[b - a] % MOD;
}
int calc(int j){
return 1ll * poww(Inv[K] , j) * Inv[N - j * K] % MOD * poww(M - j , N - j * K) % MOD * binom(M , j) % MOD * jc[N] % MOD;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
init_inv();
N = read(); M = read(); K = read();
for(int i = 0 ; i <= M ; ++i) W[i] = read();
Len = min(N / K , M);
init();
for(int i = 0 ; i <= Len ; ++i)
F[i] = 1ll * calc(i) * jc[i] % MOD;
reverse(F , F + Len + 1);
for(int i = 0 ; i <= Len ; ++i)
H[i] = Inv[i];
getInv(H , a , Len + 1);
init((Len + 1) * 2);
mul(F , a);
reverse(F , F + Len + 1);
int ans = 0;
for(int i = 0 ; i <= Len ; ++i)
ans = (ans + 1ll * F[i] * inv % MOD * Inv[i] % MOD * W[i]) % MOD;
cout << ans;
return 0;
}
LOJ2527 HAOI2018 染色 容斥、生成函数、多项式求逆的更多相关文章
- 【XSY2612】Comb Avoiding Trees 生成函数 多项式求逆 矩阵快速幂
题目大意 本题的满二叉树定义为:不存在只有一个儿子的节点的二叉树. 定义一棵满二叉树\(A\)包含满二叉树\(B\)当且经当\(A\)可以通过下列三种操作变成\(B\): 把一个节点的两个儿子同时删掉 ...
- 2019.01.01 bzoj3625:小朋友和二叉树(生成函数+多项式求逆+多项式开方)
传送门 codeforces传送门codeforces传送门codeforces传送门 生成函数好题. 卡场差评至今未过 题意简述:nnn个点的二叉树,每个点的权值KaTeX parse error: ...
- Luogu5162 WD与积木(生成函数+多项式求逆)
显然的做法是求出斯特林数,但没有什么优化空间. 考虑一种暴力dp,即设f[i]为i块积木的所有方案层数之和,g[i]为i块积木的方案数.转移时枚举第一层是哪些积木,于是有f[i]=g[i]+ΣC(i, ...
- 【BZOJ3625】【codeforces438E】小朋友和二叉树 生成函数+多项式求逆+多项式开根
首先,我们构造一个函数$G(x)$,若存在$k∈C$,则$[x^k]G(x)=1$. 不妨设$F(x)$为最终答案的生成函数,则$[x^n]F(x)$即为权值为$n$的神犇二叉树个数. 不难推导出,$ ...
- COGS 2259 异化多肽 —— 生成函数+多项式求逆
题目:http://cogs.pro:8080/cogs/problem/problem.php?pid=2259 如果构造生成函数是许多个 \( (1+x^{k}+x^{2k}+...) \) 相乘 ...
- 洛谷P4721 【模板】分治 FFT(生成函数+多项式求逆)
传送门 我是用多项式求逆做的因为分治FFT看不懂…… upd:分治FFT的看这里 话说这个万恶的生成函数到底是什么东西…… 我们令$F(x)=\sum_{i=0}^\infty f_ix^i,G(x) ...
- 牛客IOI周赛17-提高组 卷积 生成函数 多项式求逆 数列通项公式
LINK:卷积 思考的时候 非常的片面 导致这道题没有推出来. 虽然想到了设生成函数 G(x)表示最后的答案的普通型生成函数 不过忘了化简 GG. 容易推出 \(G(x)=\frac{F(x)}{1- ...
- 洛谷P4841 城市规划(生成函数 多项式求逆)
题意 链接 Sol Orz yyb 一开始想的是直接设\(f_i\)表示\(i\)个点的无向联通图个数,枚举最后一个联通块转移,发现有一种情况转移不到... 正解是先设\(g(n)\)表示\(n\)个 ...
- P4491 [HAOI2018]染色 容斥+NTT
$ \color{#0066ff}{ 题目描述 }$ 为了报答小 C 的苹果, 小 G 打算送给热爱美术的小 C 一块画布, 这块画布可 以抽象为一个长度为 \(N\) 的序列, 每个位置都可以被染成 ...
随机推荐
- concrrent类下ReentrantReadWriteLock类的原理以及使用
1.ReentrantreadWriteLock 类的介绍 Lock接口下的子类存在 ReentrantLock子类,该子类是一个线程同步处理类:ReentrantLock类的介绍详见XXX: Loc ...
- Android为TV端助力 SharedPreferences 轻量级存储!
首先在当前进程也就是当前的项目里面进行存储 SharedPreferences.Editor editor = mContext.getSharedPreferences("tvplay&q ...
- Cas 服务器 下载、编译及部署
一直想把公司运营的项目的各个子项的认证及授权统一到Cas上,从有想法到现在快一年的时间了.现在才正式着手,有兴趣的朋友一起交流学习一下.具体项目的细节不便透露,整合的大体思路为:1.开发部署Cas服务 ...
- Struts2.5学习笔记----org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter报错
Struts2.3升级到struts2.5后报错 <filter> <filter-name>struts2</filter-name> <filter-cl ...
- Redis高可用 Sentinel
官网: https://redis.io/topics/sentinel Redis的主从只能实现数据热备份的功能,主宕机后从无法自动接管服务,因此Redis推出了Sentinel的主从监控模式. S ...
- 排序算法之折半插入排序的思想以及Java实现
1 基本思想 折半插入排序(binary insertion sort)的基本原理与直接插入排序相同,不同之处在于,确定当前记录在前面有序子数组中的位置时,直接插入排序是采用顺序查找的方法,而折半插入 ...
- February 18th, 2018 Week 8th Sunday
Don't cry for what is lost. Smile for what still remains. 别为失去的哭泣,为还留在你身边的一切微笑吧. I have been told th ...
- 【工具大道】使用SSH远程登录Mac 电脑
本文地址 一.打开ssh Mac Terminal是自带SSH的,可以用whereis来看看: $ whereis ssh 但是在现有进程中找不到ssh对应的进程: $ ps aux | grep s ...
- 在线编辑器ACE Editor的使用
ACE 是一个开源的.独立的.基于浏览器的代码编辑器,可以嵌入到任何web页面或JavaScript应用程序中.ACE支持超过60种语言语法高亮,并能够处理代码多达400万行的大型文档.ACE开发团队 ...
- hTML 如何在不同页面上传递参数( 1 )
(1).一种是重定向跳转,超连<a>就是一种重定向跳转,这样的跳转request对象是传不到下一个页面的,下一个页面得到的request对象是一个新的对象,而不是上一个页面传过来的就得不到 ...