P4491 [HAOI2018] 染色

传送门:洛谷

解题思路:

写本题需要知道一个前置知识:

假设恰好选 k k k个条件的方案数为 f ( k ) f(k) f(k);先钦定选 k k k个条件,其他条件无所谓的方案数为 g ( k ) g(k) g(k)

那么存在这样的一个关系: g ( k ) = ∑ i = k n C i k f ( i ) g(k)=\sum_{i=k}^nC_{i}^kf(i) g(k)=∑i=knCikf(i)

上述式子的含义是可以枚举实际上选了几个,然后再这 i i i个中选择 k k k个作为钦定的计算方案数.因为钦定这种方式是存在重复方案的

然后使用二项式反演可以实现钦定和恰好之间的转化.

经过二项式反演可以得到: f ( k ) = ∑ i = k n C i k ∗ ( − 1 ) i − k ∗ g ( i ) f(k)=\sum_{i=k}^nC_{i}^{k}*(-1)^{i-k}*g(i) f(k)=∑i=knCik∗(−1)i−k∗g(i).

对于本题来说,我们的 g ( i ) g(i) g(i)其实很容易写出.设 g ( i ) g(i) g(i)为恰好出现 i i i个的出现次数为 s s s的颜色的方案数.不难写出 g ( i ) = C m i A n s ∗ i ( s ∗ i ) ! ∗ ( m − i ) n − s ∗ i g(i)=C_m^i\frac{A_n^{s*i}}{(s*i)!}*(m-i)^{n-s*i} g(i)=Cmi(s∗i)!Ans∗i∗(m−i)n−s∗i

然后我们反演一下就得到了: f ( k ) = ∑ i = k n C i k ( − 1 ) i − k g ( i ) f(k)=\sum_{i=k}^{n}C_{i}^{k}(-1)^{i-k}g(i) f(k)=i=k∑nCik(−1)i−kg(i)

稍微化解一下就能得到:
f ( k ) ∗ k ! = ∑ i = k n g ( i ) ∗ i ! ∗ ( − 1 ) i − k ( i − k ) ! f(k)*k!=\sum_{i=k}^ng(i)*i!*\frac{(-1)^{i-k}}{(i-k)!} f(k)∗k!=i=k∑ng(i)∗i!∗(i−k)!(−1)i−k

然后我们设 G ( i ) = g ( i ) ∗ i ! , H ( i ) = ( − 1 ) i − k ( i − k ) ! G(i)=g(i)*i!,H(i)=\frac{(-1)^{i-k}}{(i-k)!} G(i)=g(i)∗i!,H(i)=(i−k)!(−1)i−k就能得到 F ( k ) = ∑ i = k n G ( i ) ∗ H ( i − k ) F(k)=\sum_{i=k}^nG(i)*H(i-k) F(k)=i=k∑nG(i)∗H(i−k)

我们使用经典套路将 G G G数组 r e v e r s e reverse reverse一下,就得到了 F ( K ) = ∑ i = k n G ( n − i ) ∗ H ( i − k ) F(K)=\sum_{i=k}^nG(n-i)*H(i-k) F(K)=i=k∑nG(n−i)∗H(i−k)
PS:需要注意的是此时翻转的n可以不为n,不熟悉的人可能会搞不清楚

然后这是一道很显然的卷积式子.此时我们使用 N T T NTT NTT卷一下即可.此时我们的的 f ( k ) f(k) f(k)就是卷积完之后第 n − k n-k n−k项的系数.


下面是具体的代码部分:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
inline void print(__int128 x){
	if(x<0) {putchar('-');x=-x;}
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
#define maxn 10001000
#define int long long
const int mod=1004535809;
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int qpow(int a,int b) {
	int ans=1;
	while(b) {
		if(b&1) ans=ans*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return ans;
}
int rev[maxn];
void NTT(int *a,int n,int inv) {
	for(int i=0;i<=n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int len=1;len<=(n>>1);len<<=1) {
		int gn=qpow(inv==1?3:qpow(3,mod-2),(mod-1)/(len<<1));
		for(int i=0;i<=n;i+=(len<<1)) {
			int g0=1;
			for(int j=0;j<=len-1;j++) {
				int x=a[i+j],y=a[i+j+len]*g0%mod;
				a[i+j]=(x+y)%mod;a[i+j+len]=((x-y)%mod+mod)%mod;
				g0=g0*gn%mod;
			}
		}
	}
}
int fac[maxn],in_fac[maxn];
void init(int limit) {
	fac[0]=1;
	for(int i=1;i<=limit;i++) {
		fac[i]=fac[i-1]*i%mod;
	}
	in_fac[limit]=qpow(fac[limit],mod-2);
	for(int i=limit-1;i>=0;i--) {
		in_fac[i]=in_fac[i+1]*(i+1)%mod;
	}
}
int C(int a,int b) {
	return fac[a]*in_fac[b]%mod*in_fac[a-b]%mod;
}
int A(int a,int b) {
	return fac[a]*in_fac[a-b]%mod;
}
int w[maxn];int g[maxn];int G[maxn],H[maxn];int F[maxn];
signed main() {
	int n=read();int m=read();int s=read();
	init(max(n,m));
	for(int i=0;i<=m;i++) {
		w[i]=read();
	}
	int k=min(m,n/s);
	for(int i=0;i<=k;i++) {
		g[i]=C(m,i)*A(n,s*i)%mod*qpow(in_fac[s],i)%mod*qpow(m-i,n-s*i)%mod;
	}
	for(int i=0;i<=k;i++) {
		G[i]=g[i]*fac[i]%mod;
		H[i]=((i&1?-1:1)*in_fac[i]%mod+mod)%mod;
	}
	reverse(G,G+k+1);
	int limit=1,len=0;
	while(limit<=k+k) limit<<=1,len++;
	for(int i=0;i<=limit;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
	NTT(G,limit,1);NTT(H,limit,1);
	for(int i=0;i<=limit;i++) F[i]=G[i]*H[i]%mod;
	NTT(F,limit,-1);
	int inv=qpow(limit,mod-2);
	for(int i=0;i<=limit;i++) {
		F[i]=F[i]*inv%mod;
	}
	int ans=0;
	for(int i=0;i<=k;i++) {
		ans=(ans+F[k-i]*in_fac[i]%mod*w[i]%mod)%mod;
	}
	cout<<ans<<endl;
	return 0;
}
相关推荐
草莓熊Lotso1 小时前
《算法闯关指南:优选算法--模拟》--41.Z 字形变换,42.外观数列
开发语言·c++·算法
啊吧怪不啊吧1 小时前
算法王冠上的明珠——动态规划之斐波那契数列问题
大数据·算法·动态规划
无敌最俊朗@8 小时前
力扣hot100-206反转链表
算法·leetcode·链表
Kuo-Teng8 小时前
LeetCode 279: Perfect Squares
java·数据结构·算法·leetcode·职场和发展
王哈哈^_^8 小时前
YOLO11实例分割训练任务——从构建数据集到训练的完整教程
人工智能·深度学习·算法·yolo·目标检测·机器学习·计算机视觉
檐下翻书1739 小时前
从入门到精通:流程图制作学习路径规划
论文阅读·人工智能·学习·算法·流程图·论文笔记
CoderYanger9 小时前
B.双指针——3194. 最小元素和最大元素的最小平均值
java·开发语言·数据结构·算法·leetcode·职场和发展·1024程序员节
小曹要微笑10 小时前
STM32各系列时钟树详解
c语言·stm32·单片机·嵌入式硬件·算法
前进的李工11 小时前
LeetCode hot100:094 二叉树的中序遍历:从递归到迭代的完整指南
python·算法·leetcode·链表·二叉树
麦麦大数据12 小时前
F049 知识图谱双算法推荐在线学习系统vue+flask+neo4j之BS架构开题论文全源码
学习·算法·知识图谱·推荐算法·开题报告·学习系统·计算机毕业设计展示