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\) 的序列, 每个位置都可以被染成 ...
随机推荐
- Android为TV端助力 doc里面adb连接出现问题的解决方法
第一保证连接的两边都是有网的 第二 就是网上常说的1.adb kill-server 2.adb start-server 3.adb remount 但是在运行adb remount有可能会提示 ...
- Kotlin入门(29)任务Runnable
任务Runnable定义了一个可以独立运行的代码片段,通常用于界面控件的延迟处理,比如有时为了避免同时占用某种资源造成冲突,有时则是为了反复间隔刷新界面从而产生动画效果.运行一个任务也有多种形式,既可 ...
- 简单 PHP + MySQL 数据库动态网站制作 -- 摘抄
在这篇文章中,我尽量用最浅显易懂的语言来说明使用 PHP, MySQL 制作一个动态网站的基本技术.阅读本文需要简单的 HTML 基础知识和(任一编程语言的)编程基础知识(例如变量.值.循环.语句块的 ...
- ssh框架总结之action接收参数的三种方式
页面将参数传递给action的三种方式 一是通过属性传值: 将页面和action的的属性值保持一致,在action上写上该属性的set和get方法,这样在页面提交参数的时候,action就会调用set ...
- Cygwin下编译的程序不使用Cygwin.dll即可运行的命令 及常用命令简介
cc -mno-cygwin foo.c 1.$ ps PS的相关用法: QuoteUsage ps [-aefl] [-u uid]-f = show process uids, ppids-l = ...
- Python基于皮尔逊系数实现股票预测
# -*- coding: utf-8 -*- """ Created on Mon Dec 2 14:49:59 2018 @author: zhen "&q ...
- 洗礼灵魂,修炼python(89)-- 知识拾遗篇 —— 进程
进程 1.含义:计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位.说白了就是一个程序的执行实例. 执行一个程序就是一个进程,比如你打开浏览器看到我的博客,浏览器本身是一 ...
- Android重复依赖解决办法
参考文章:https://blog.csdn.net/qq_24216407/article/details/72842614 在build.gradle引用了Vlc的安卓包:de.mrmaffen: ...
- php防范
针对 PHP 的网站主要存在下面几种攻击方式: 1.命令注入(Command Injection) 2.eval 注入(Eval Injection) 3.客户端脚本攻击(Script Inserti ...
- 微信小程序多层嵌套循环,二级数组遍历
小程序中的遍历循环类似于angularJS的遍历. 二级数组遍历有一个坑.二级遍历wx:for循环的时候,需要注意.(代码如下) JS代码: data: { groups: [ [ { title: ...