[HAOI2018][bzoj5306] 染色 [容斥原理+NTT]
题面
思路
这道题的核心在于“恰好有$k$种颜色占了恰好$s$个格子”
这些“恰好”,引导我们去思考,怎么求出总的方案数呢?
分开考虑
考虑把恰好有$s$个格子的颜色,和不是$s$个颜色的格子分开来考虑
那么,显然答案可以用这样的一个式子表示:
令$lim=min(\lfloor\frac ns\rfloor,m)$,那么:
$ans=\sum_{i=0}{lim}w_iC_miC_n{is}\frac{(is)!}{(s!)i}g(m-i,n-is)$
其中$g(i,j)$表示把$i$种颜色放进$j$个格子里面,没有“恰好占了$s$个格子”的颜色的方案总数
这个$g$显然可以容斥原理来做
容斥原理
考虑对$g$用容斥原理推导,可得到如下公式:
$g(i,j)=\sum_{k=0}i(-1)kC_ikC_j{ks}\frac{(ks)!}{(s!)k}(i-k){j-ks}$
把这个公式代回原式,可得到:
$ans=\sum_{i=0}{lim}w_iC_miC_n{is}\frac{(is)!}{(s!)i}\sum_{j=0}{lim-i}(-1)jC_{m-i}jC_{n-is}{js}\frac{(js)!}{(s!)j}(m-i-j){n-is-js}$
我们发现这个玩意儿好像比较复杂,没有什么特殊性质
难道这条路走不通?
构造卷积
我们发现后面的这个代表$g$的这一部分里面,有很多的$j$在,
那么我们想一想,$j$的枚举是所有比当前的$i$小的,那么只要把$j$用另一个东西:$j-i$替代了,然后把$j$变成所有比当前的大的,那么是不是和这个式子等价了呢?
说干就干!
$ans=\sum_{i=0}{lim}w_iC_miC_n{is}\frac{(is)!}{(s!)i}\sum_{j=lim-i}{lim}(-1){j-i}C_{m-i}{j-i}C_{n-is}{js-is}\frac{(js-is)!}{(s!){j-i}}(m-j){n-js}$
诶,这样一变化......后面的式子里这么多$j-i$,前面又是$i$,这让我们想到了什么?
处理卷积啊!
变换枚举方式
我们先把这些个烦人的组合数拆成阶乘的形式
$ans=\sum_{i=0}{lim}w_i\frac{m!n!}{i!(is)!(m-i)!(n-is)!}\frac{(is)!}{(s!)i}\sum_{j=lim-i}{lim}(-1){j-i}\frac{(m-i)!(n-is)!}{(j-i)!(js-is)!(m-j)!(n-js)!}\frac{(js-is)!}{(s!){j-i}}(m-j){n-js}$
我们把左边的项挪进右边的最后一个sigma里面,然后把$ij$的枚举顺序反过来,得到:
$ans=\sum_{j=0}{lim}\sum_{i=0}{j}w_i\frac{m!n!}{i!(is)!(m-i)!(n-is)!}\frac{(is)!}{(s!)i}(-1){j-i}\frac{(m-i)!(n-is)!}{(j-i)!(js-is)!(m-j)!(n-js)!}\frac{(js-is)!}{(s!){j-i}}(m-j){n-js}$
发现有很多项可以消掉了
$ans=\sum_{j=0}{lim}\sum_{i=0}{j}w_i\frac{m!n!}{i!(s!)i}(-1){j-i}\frac{1}{(j-i)!(m-j)!(n-js)!}\frac{1}{(s!){j-i}}(m-j){n-js}$
再合并一下各个分数线
$ans=\sum_{j=0}{lim}\sum_{i=0}{j}(-1){j-i}\frac{w_im!n!}{i!(s!)j(j-i)!(m-j)!(n-js)!}(m-j)^{n-js}$
把所有只含$j$的提到前面一个sigma去
$ans=\sum_{j=0}{lim}\frac{m!n!(m-j){n-js}}{(s!)j(m-j)!(n-js)!}\sum_{i=0}{j}\frac{(-1)^{j-i}w_i}{i!(j-i)!}$
发现后面是一个卷积的形式!
设$a(i)=\frac{w_i}{i!},b(i)=\frac{(-1)^i}{i!}$
那么:
$ans=\sum_{j=0}{lim}\frac{m!n!(m-j){n-js}}{(s!)^j(m-j)!(n-js)!}(a\ast b)(j)$
所有的逆元、阶乘都可以$O(n)$预处理,卷积使用模数为998244353的$NTT$来实现,总时间复杂度为$O(n+limlog_2lim)$
Code:
我的代码好像常数比较大......如果有知道怎么优化的dalao,麻烦在评论区帮忙指出一下!
不胜感激!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline ll read(){
ll re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
ll MOD=1004535809,g=3,f[10000010],finv[10000010],w[10000010],invlimit;
ll qpow(ll a,ll b){
ll re=1;
while(b){
if(b&1) re=re*a%MOD;
a=a*a%MOD;b>>=1;
}
return re;
}
ll A[400010],B[400010],r[400010],n,limit,cnt,m,S,N,invg;
void init(){
limit=1;cnt=0;ll i;
while(limit<=(N<<1)) limit<<=1,cnt++;
for(i=0;i<limit;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));
f[1]=finv[1]=f[0]=finv[0]=1;
for(i=2;i<=max(max(n,m),S);i++){
f[i]=f[i-1]*i%MOD;
}
finv[max(max(n,m),S)]=qpow(f[max(max(n,m),S)],MOD-2);
for(i=max(max(n,m),S);i>=1;i--) finv[i-1]=finv[i]*i%MOD;
invlimit=qpow(limit,MOD-2);invg=qpow(g,MOD-2);
}
void ntt(ll *a,ll type){
ll i,j,k,mid,len;ll y,w,wn;
for(i=0;i<limit;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(mid=1;mid<limit;mid<<=1){
len=mid<<1;
wn=qpow(((type==1)?g:invg),(MOD-1)/len);
for(j=0;j<limit;j+=len){
w=1;
for(k=0;k<mid;k++,w=w*wn%MOD){
y=a[j+k+mid]*w%MOD;
a[j+k+mid]=(a[j+k]-y+MOD)%MOD;
a[j+k]=(a[j+k]+y)%MOD;
}
}
}
if(type==-1) for(i=0;i<limit;i++) a[i]=(a[i]*invlimit%MOD);
}
int main(){
n=read();m=read();S=read();N=min(n/S,m);ll i;
for(i=0;i<=m;i++) w[i]=read();
init();
for(i=0;i<=N;i++){
A[i]=finv[i]*w[i]%MOD;
B[i]=((i&1)?MOD-1:1)*finv[i]%MOD;
}
ntt(A,1);ntt(B,1);
for(i=0;i<limit;i++) A[i]=A[i]*B[i]%MOD;
ntt(A,-1);
ll ans=0;
for(i=0;i<=N;i++){
ans=(ans+(finv[m-i]*finv[n-i*S]%MOD*qpow(finv[S],i)%MOD*qpow(m-i,n-i*S)%MOD*A[i]))%MOD;
}
printf("%lld\n",ans*f[n]%MOD*f[m]%MOD);
}
[HAOI2018][bzoj5306] 染色 [容斥原理+NTT]的更多相关文章
- [BZOJ5306] [HAOI2018]染色(容斥原理+NTT)
[BZOJ5306] [HAOI2018]染色(容斥原理+NTT) 题面 一个长度为 n的序列, 每个位置都可以被染成 m种颜色中的某一种. 如果n个位置中恰好出现了 S次的颜色有 K种, 则小 C ...
- 【BZOJ5306】[HAOI2018]染色(NTT)
[BZOJ5306]染色(NTT) 题面 BZOJ 洛谷 题解 我们只需要考虑每一个\(W[i]\)的贡献就好了 令\(lim=min(M,\frac{N}{S})\) 那么,开始考虑每一个\(W[i ...
- 「HAOI2018」染色 解题报告
「HAOI2018」染色 是个套路题.. 考虑容斥 则恰好为\(k\)个颜色恰好为\(c\)次的贡献为 \[ \binom{m}{k}\sum_{i\ge k}(-1)^{i-k}\binom{m-k ...
- 【bzoj3456】城市规划 容斥原理+NTT+多项式求逆
题目描述 求出n个点的简单(无重边无自环)无向连通图数目mod 1004535809(479 * 2 ^ 21 + 1). 输入 仅一行一个整数n(<=130000) 输出 仅一行一个整数, 为 ...
- BZOJ5306 HAOI2018染色(容斥原理+NTT)
容易想到枚举恰好出现S次的颜色有几种.如果固定至少有i种恰好出现S次,那么方案数是C(M,i)·C(N,i*S)·(M-i)N-i*S·(i*S)!/(S!)i,设为f(i). 于是考虑容斥,可得恰好 ...
- LOJ #2527 Luogu P4491「HAOI2018」染色
好像网上没人....和我推出....同一个式子啊..... LOJ #2527 Luogu P4491 题意 $ n$个格子中每个格子可以涂$ m$种颜色中的一种 若有$ k$种颜色恰好涂了$ s$格 ...
- 【LOJ】#2527. 「HAOI2018」染色
题解 简单容斥题 至少选了\(k\)个颜色恰好出现\(S\)次方案数是 \(F[k] = \binom{M}{k} \frac{N!}{(S!)^{k}(N - i * S)!}(M - k)^{N ...
- Luogu5401 CTS2019珍珠(生成函数+容斥原理+NTT)
显然相当于求有不超过n-2m种颜色出现奇数次的方案数.由于相当于是对各种颜色选定出现次数后有序排列,可以考虑EGF. 容易构造出EGF(ex-e-x)/2=Σx2k+1/(2k+1)!,即表示该颜色只 ...
- HAOI2018 简要题解
这套题是 dy, wearry 出的.学长好强啊,可惜都 \(wc\) 退役了.. 话说 wearry 真的是一个计数神仙..就没看到他计不出来的题...每次考他模拟赛总有一两道毒瘤计数TAT 上午的 ...
随机推荐
- 【转】IOS基础:深入理解Objective-c中@class的含义
objective-c中,当一个类使用到另一个类时,并且在类的头文件中需要创建被引用的指针时, 如下面代码: A.h文件 #import "B.h" @interface A : ...
- angular4 学习日志(一 依赖注入)
1.创建一个服务,为了好管理建一个名叫services的文件夹管理所有服务: ng g service services\person 2.在服务中定义一个person 类 : 3.在app.mdul ...
- 高阶函数 -------JavaScript
高阶函数 本文摘要:http://www.liaoxuefeng.com/ JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作 ...
- Jmeter压力测试工具基本使用
转:https://blog.csdn.net/envyfan/article/details/42715779
- 软件杯python-flask遇到的坑有感!
大三下,对于我考研的人来说,时间不要太紧张,参加软件杯也是系主任要求,题目是公共地点人流量的检测,个人还是个菜鸟,但是把遇到的一些大家可能不小心会出现的问题贴出来,困扰我很久,还没睡好觉!!! Que ...
- java面向对象思想1
1.面向对象是面向过程而言.两者都是一种思想.面向过程:强调的是功能行为.(强调过程.动作)面向对象:将功能封装进对象,强调了具备了功能的对象.(强调对象.事物)面向对象是基于面向过程的.将复杂的事情 ...
- 第22题:链表中倒数第k个结点
题目描述 题目:输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点.例如一个链表有6个结点,从头结点开始它们的值依次是1.2.3.4.5 ...
- 牛客小白月赛5 I 区间 (interval) 【前缀和】
链接:https://www.nowcoder.com/acm/contest/135/I 题目描述 Apojacsleam喜欢数组. 他现在有一个n个元素的数组a,而他要对a[L]-a[R]进行M次 ...
- 二十三、MySQL 事务
MySQL 事务 MySQL 事务主要用于处理操作量大,复杂度高的数据.比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数 ...
- while循环中continue和break的区别
除了满足while条件外,还有两种方法可以终止循环,它们分别是break和continue.它们唯一的区别是break跳出整个循环,直接执行下面的代码了;而continue是终止当次循环,不执行下面的 ...