ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

HAOI2018 染色

2021-01-26 11:02:39  阅读:223  来源: 互联网

标签:ifac int 染色 mo maxn sum HAOI2018 LL


设 G[k] 表示恰好出现 S 次的颜色恰好有 k 种, 数值为方案总数。设 n = min(M, floor(N/S)), 这道题的答案就是 Σ1≤i≤n Wi × G[i]。

设 F[k] 表示恰好出现 S 次的颜色至少有 k 种, 数值为方案总数。这个很好算, 先选出颜色 \(\dbinom Mk\), 然后把没选出的颜色当成无色, 按照可重排列的方式构造出所有局面 \(\dfrac{N!}{(S!)^k(N-S*k)!}\), 最后把所有无色填上颜色 \((M-k)^{N-S*k}\)。

很显然的,

\[F[k] = \sum_{i = k}^n \binom ik * G[i] \]

为什么会有二项式系数呢?因为同一个方案会以这些数量的 “F特有的” 方式被观测到。

现在二项式反演, 得到:

\[G[k] = \sum_{i = k}^n (-1)^{n - k}\binom ik F[i] \]

现在得到了一个 O(n2) 的算法。


拆解一下:

\[G[k] = \sum_{i=k}^n (-1)^{i-k}\frac{i!}{k!(i-k)!}F[i] \\ G[k] * k! = \sum_{i=k}^n \left(\frac{(-1)^{i-k}}{(i-k)!}\right)*(F[i]*i!) \]

设 \(A[i] = \dfrac{(-1)^i}{i!}\), \(B[i] = F[i] * i!\), 那么

\[G[k] * k! = \sum_{i = k}^n A[i - k] * B[i] \]

把 B 翻转一下变成 \(\hat B[n - i] = B[i]\), 就有:

\[G[k] * k! = \sum_{i = k}^n A[i-k] * \hat B[n - i] \\ G[k] * k! = \sum_{i = 0}^{n-k} A[i] * \hat B[n - k - i] \]

这是 \((A * \hat B)[n - k]\)。

本题做法就清晰明了了。


对于模数, 首先, 测试的结果是模数是质数, 模数减一的质因数分解是 221 × 479。

原根暂时不会, 就拿题解的原根来用把(

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;

#define int long long

const int maxn = 4e5 + 23, mo = 1004535809;
int ksm(int a, int b) {
	int res = 1;
	for(; b; b = b>>1, a = ((LL)a * a) % mo)
		if(b & 1) res = ((LL)res * a) % mo;
	return res;
}
const int g = 3, ig = ksm(g, mo - 2);

int N, M, S;
int fac[10000003], ifac[10000003];
int C(int n, int m) {
	return m > n ? 0 : (LL)fac[n] * (LL)ifac[m] % mo * (LL)ifac[n - m] % mo;
}
int calcF(int i) {
	return (LL)C(M, i) * fac[N] % mo * ksm(ifac[S], i) % mo
						* ifac[N - S * i] % mo * ksm(M - i, N - S * i) % mo;
}

int rv[maxn];
void NTT(int *a, int n, int type) {
	for(int i = 0; i < n; ++i) if(i < rv[i]) swap(a[i], a[rv[i]]);
	for(int m = 2; m <= n; m = m << 1) {
		int w = ksm(type == 1 ? g : ig, (mo - 1) / m);
		for(int i = 0; i < n; i += m) {
			int tmp = 1;
			for(int j = 0; j < (m >> 1); ++j) {
				int p = a[i + j], q = (LL)tmp * a[i + j + (m >> 1)];
				a[i + j] = (p + q) % mo, a[i + j + (m >> 1)] = (p - q + mo) % mo;
				tmp = (LL)tmp * w % mo;
			}
		}
	}
	if(type == -1) {
		int Inv = ksm(n, mo - 2);
		for(int i = 0; i < n; ++i) a[i] = (LL)a[i] * Inv % mo;
	}
}

int n, W[maxn];
int A[maxn], B[maxn], G[maxn];

signed main()
{
	scanf("%lld%lld%lld", &N, &M, &S);
	for(int i = 0; i <= M; ++i) scanf("%lld", &W[i]);
	n = min(M, N / S);
	int D = max(N, max(M, S));
	fac[0] = 1;
	for(int i = 1; i <= D; ++i) fac[i] = (LL)i * (LL)fac[i - 1] % mo;
	ifac[D] = ksm(fac[D], mo - 2);
	for(int i = D; i > 0; --i) ifac[i - 1] = (LL)ifac[i] * (LL)i % mo;
	
	for(int i = 0; i <= n; ++i) {
		A[i] = ifac[i]; if(i & 1) A[i] = (mo - A[i]);
		B[i] = (LL)fac[i] * (LL)calcF(i) % mo;
	}
	reverse(B, B + n + 1);
	
	int len = 1; while(len < ((n+1) << 1)) len = len << 1;
	for(int i = 1; i < len; ++i) rv[i] = (rv[i>>1]>>1) | ((i&1) ? len>>1 : 0);
	NTT(A, len, 1), NTT(B, len, 1);
	for(int i = 0; i < len; ++i) G[i] = (LL)A[i] * (LL)B[i] % mo;
	NTT(G, len, -1);
	reverse(G, G + n + 1);
	for(int i = 0; i <= n; ++i) G[i] = (LL)G[i] * ifac[i] % mo;
	long long ans = 0ll;
	for(int i = 0; i <= n; ++i) ans = ans + ((LL)G[i] * (LL)W[i] % mo), ans = ans % mo;
	cout << (ans % mo + mo) % mo;
	return 0;
}

标签:ifac,int,染色,mo,maxn,sum,HAOI2018,LL
来源: https://www.cnblogs.com/tztqwq/p/14329160.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有