题解 [QkOI#R1] Quark and Graph

题面

已知一个 n n n 个点的无权无向图中 1 号点到 i ( 1 ≤ i ≤ n ) i (1\le i\le n) i(1in) 号点的最短路 d i d_i di,试求恰好 m m m 条边的方案数。

数据范围: n ≤ 1 0 5 , m ≤ 2 ⋅ 1 0 5 n\le 10^5, m\le 2\cdot 10^5 n105,m2105,令 t i = ∑ j [ d j = i ] t_i=\sum_j [d_j=i] ti=j[dj=i] 则有 ∑ i t i t i + 1 ≤ 2 ⋅ 1 0 5 \sum_i t_it_{i+1}\le 2\cdot 10^5 ititi+12105

题解

将图变成分层图,边分为两部分,一部分是每层间的边,一部分是相邻两层的边,所以不难知道答案是

[ x m ] ∏ i = 0 D − 1 ( ∑ n ≥ 1 x n ( a i n ) ) a i + 1 ∏ i = 0 n ( ∑ n ≥ 0 x n ( a i ( a i − 1 ) / 2 n ) ) [x^m] \prod_{i=0}^{D-1} \left( \sum_{n\ge 1} x^n \binom{a_i}{n} \right)^{a_{i+1}} \prod_{i=0}^n \left( \sum_{n\ge 0} x^n \binom{a_i(a_i-1)/2}{n} \right) [xm]i=0D1(n1xn(nai))ai+1i=0n(n0xn(nai(ai1)/2))

这个生成函数是

( ∏ i = 0 D − 1 ( ( x + 1 ) a i − 1 ) a i + 1 ) ( x + 1 ) ∑ i = 0 D a i ( a i − 1 ) / 2 \left( \prod_{i=0}^{D-1} ((x+1)^{a_i} -1) ^{a_{i+1}} \right) (x+1)^{\sum\limits_{i=0}^D a_i(a_i-1)/2 } (i=0D1((x+1)ai1)ai+1)(x+1)i=0Dai(ai1)/2

接下来应当注意到题目中奇怪的数据范围 ∑ i a i a i + 1 ≤ 2 ⋅ 1 0 5 \sum\limits_i a_ia_{i+1} \le 2\cdot 10^5 iaiai+12105,令其为 S S S,则可以发现左侧的乘积式的长度是 S S S 级别的,所以我猜测这里应该已经能够做了,但我不是太清楚。

言归正传,接下来使用套路:

∏ i = 0 D − 1 ( ( x + 1 ) a i − 1 ) a i + 1 = exp ⁡ ∑ i = 0 D − 1 a i + 1 ln ⁡ ( ( x + 1 ) a i − 1 ) \prod_{i=0}^{D-1} ((x+1)^{a_i} -1) ^{a_{i+1}} = \exp \sum_{i=0}^{D-1} a_{i+1} \ln ((x+1)^{a_i} -1) i=0D1((x+1)ai1)ai+1=expi=0D1ai+1ln((x+1)ai1)

好吧这是个什么玩意,ln 里边的形式幂级数一次项根本就是 0。

所以重新做一遍,我们做换元 z = x + 1 z=x+1 z=x+1,再做一次:

G ( z ) = ∏ i = 0 D − 1 ( z a i − 1 ) a i + 1 = ( − 1 ) ∑ i = 1 D a i exp ⁡ ( ∑ i = 0 D − 1 a i + 1 ln ⁡ ( 1 − z a i ) ) = ( − 1 ) ∑ i = 1 D a i exp ⁡ ( − ∑ i = 0 D − 1 a i + 1 ∑ j ≥ 1 z j a i j ) G(z)=\prod_{i=0}^{D-1} (z^{a_i} - 1) ^{a_{i+1}} = (-1)^{\sum\limits_{i=1}^D a_i} \exp \left( \sum_{i=0}^{D-1} a_{i+1} \ln (1-z^{a_i}) \right) = (-1)^{\sum\limits_{i=1}^D a_i} \exp \left( - \sum_{i=0}^{D-1} a_{i+1} \sum_{j\ge 1} \frac{z^{ja_i}}{j} \right) G(z)=i=0D1(zai1)ai+1=(1)i=1Daiexp(i=0D1ai+1ln(1zai))=(1)i=1Daiexp(i=0D1ai+1j1jzjai)

如果我们要求出这个形式幂级数的前 L L L 项,由于 exp 里的形式幂级数的有效项为 O ( ∑ i L i ) = O ( L log ⁡ L ) O(\sum_i \frac{L}{i})=O(L\log L) O(iiL)=O(LlogL),因此求出这个式子的复杂度是 O ( L log ⁡ L ) O(L\log L) O(LlogL)

现在我们需要还原换元之前的式子,很明显我们需要知道这个形式幂级数(多项式)所有的信息,所以求出 [ z 0 ∼ S ] G ( z ) [z^{0\sim S}]G(z) [z0S]G(z)

F ( x ) = G ( x + 1 ) = ∑ i ≥ 0 g i ( x + 1 ) i = ∑ i ≥ 0 g i ∑ j ≥ 0 ( i j ) x j = ∑ j ≥ 0 x j ∑ i ≥ 0 g i ( i j ) = ∑ j ≥ 0 x j ∑ i ≥ 0 g i + j ( i + j ) ! i ! j ! = ∑ j ≥ 0 x j j ! ∑ i ≥ 0 h S − i − j i ! \begin{aligned} F(x) = G(x+1) = \sum_{i\ge 0} g_i (x+1)^i = \sum_{i\ge 0} g_i \sum_{j\ge 0} \binom{i}{j} x^j = \sum_{j\ge 0} x^j \sum_{i\ge 0} g_i \binom{i}{j} \\ = \sum_{j\ge 0} x^j \sum_{i\ge 0} g_{i+j} \frac{(i+j)!}{i!j!} = \sum_{j\ge 0} \frac{x^j}{j!} \sum_{i\ge 0} \frac{h_{S-i-j}}{i!} \end{aligned} F(x)=G(x+1)=i0gi(x+1)i=i0gij0(ji)xj=j0xji0gi(ji)=j0xji0gi+ji!j!(i+j)!=j0j!xji0i!hSij

其中 h i = g S − i ( S − i ) ! h_i=g_{S-i} (S-i)! hi=gSi(Si)!,所以这里也 O ( S log ⁡ S ) O(S\log S) O(SlogS) 解决。

现在要说刚才被忽略的右半部分,理性分析一下发现项数不多所以我们维护下降幂就好了。

时间复杂度 O ( n + S log ⁡ S + m log ⁡ m ) O(n + S\log S + m\log m) O(n+SlogS+mlogm)

代码

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

typedef long long ll;
#define ri register int

const int MAXN = 1000005, MOD = 998244353, Gen = 3;

ll q_pow(ll a, ll b, ll p = MOD) {
	ll ret = 1;
	for (; b; a = a * a % p, b >>= 1) if (b & 1) ret = ret * a % p;
	return ret;
}
ll inv(ll x, ll p = MOD) { return q_pow(x, p - 2, p); }

inline int add(int x, int y) { return x + y > MOD ? x + y - MOD : x + y; }
inline int dec(int x, int y) { return x - y < 0 ? x - y + MOD : x - y; }
inline void Add(int& x, int y) { x += y; if (x >= MOD) x -= MOD; }
inline void Dec(int& x, int y) { x -= y; if (x < 0) x += MOD; }

int rev[21][MAXN], fac[MAXN], ifac[MAXN], I[MAXN];
int rt[MAXN], irt[MAXN];
void Dft(int* A, int LIM, int L) {
	for (ri i = 0; i < LIM; ++i) if (i < rev[L][i]) swap(A[i], A[rev[L][i]]);
	for (ri l = 2; l <= LIM; l <<= 1) {
		for (ri i = 0; i < LIM; i += l) {
			for (ri j = 0; j < (l >> 1); ++j) {
				ll x = A[j | i], y = (ll)A[j | i | (l >> 1)] * rt[l | j] % MOD;
				A[j | i] = add(x, y), A[j | i | (l >> 1)] = dec(x, y);
			}
		}
	}
}
void iDft(int* A, int LIM, int L) {
	for (ri i = 0; i < LIM; ++i) if (i < rev[L][i]) swap(A[i], A[rev[L][i]]);
	for (ri l = 2; l <= LIM; l <<= 1) {
		for (ri i = 0; i < LIM; i += l) {
			for (ri j = 0; j < (l >> 1); ++j) {
				ll x = A[j | i], y = (ll)A[j | i | (l >> 1)] * irt[l | j] % MOD;
				A[j | i] = add(x, y), A[j | i | (l >> 1)] = dec(x, y);
			}
		}
	}
	int invLIM = inv(LIM);
	for (ri i = 0; i < LIM; ++i) A[i] = 1ll * A[i] * invLIM % MOD;
}
void Multiply(int* A, int* B, int LIM, int L) {
	Dft(A, LIM, L), Dft(B, LIM, L);
	for (ri i = 0; i < LIM; ++i) A[i] = 1ll * A[i] * B[i] % MOD;
	iDft(A, LIM, L);
}

int Tinv[MAXN];
void Inverse(const int* F, int* G, int LIM, int L) {
	G[0] = inv(F[0]);
	for (ri lim = 2, l = 1; lim <= LIM; lim <<= 1, ++l) {
		for (ri i = 0; i < lim; ++i) Tinv[i] = F[i];
		for (ri i = lim; i < (lim << 1); ++i) Tinv[i] = 0;
		Dft(Tinv, lim << 1, l + 1), Dft(G, lim << 1, l + 1);
		for (ri i = 0; i < (lim << 1); ++i) G[i] = dec(add(G[i], G[i]), 1ll * Tinv[i] * G[i] % MOD * G[i] % MOD);
		iDft(G, lim << 1, l + 1);
		for (ri i = lim; i < (lim << 1); ++i) G[i] = 0;
	}
}
void Derivative(int* F, int LIM) {
	for (ri i = 0; i < LIM - 1; ++i) F[i] = 1ll * F[i + 1] * (i + 1) % MOD;
	F[LIM - 1] = 0;
}
void Inter(int* F, int LIM) {
	for (ri i = LIM - 1; i >= 1; --i) F[i] = 1ll * F[i - 1] * I[i] % MOD;
	F[0] = 0;
}
int Tln[MAXN];
void Ln(int* F, int LIM, int L) {
	Inverse(F, Tln, LIM, L);
	Derivative(F, LIM);
	Multiply(F, Tln, LIM << 1, L + 1);
	Inter(F, LIM);
	for (ri i = 0; i < (LIM << 1); ++i) Tln[i] = 0;
}

int Texp[MAXN];
void Exp(const int* F, int* G, int LIM, int L) {
	G[0] = 1;
	for (ri lim = 2, l = 1; lim <= LIM; lim <<= 1, ++l) {
		for (ri i = 0; i < lim; ++i) Texp[i] = G[i];
		for (ri i = lim; i < (lim << 1); ++i) Texp[i] = 0;
		Ln(G, lim, l);
		for (ri i = 0; i < lim; ++i) G[i] = dec(F[i], G[i]);
		G[0] = add(G[0], 1);
		Multiply(G, Texp, lim << 1, l + 1);
		for (ri i = lim; i < (lim << 1); ++i) G[i] = 0;
	}
}

int N, M, LIM = 1, L, a[MAXN], b[MAXN], D, S;
int F[MAXN], G[MAXN], H[MAXN];
int main() {
	scanf("%d%d", &N, &M); int t;
	for (int i = 1; i <= N; ++i) scanf("%d", &t), ++a[t], D = max(t, D);
	for (int i = 0; i < D; ++i) S += a[i] * a[i + 1];

	while (LIM <= max(M, S) + 2) {
        LIM <<= 1, ++L;
        for (ri i = 0; i < LIM; ++i) rev[L][i] = (rev[L][i >> 1] >> 1) | ((i & 1) << (L - 1));
        irt[LIM] = rt[LIM] = 1; int Wn = q_pow(Gen, (MOD - 1) / LIM), iWn = inv(Wn);
        for (ri i = 1; i < (LIM >> 1); ++i) rt[i | LIM] = 1ll * rt[(i - 1) | LIM] * Wn % MOD, irt[i | LIM] = 1ll * irt[(i - 1) | LIM] * iWn % MOD;
    }
    LIM <<= 1, ++L;
    for (ri i = 0; i < LIM; ++i) rev[L][i] = (rev[L][i >> 1] >> 1) | ((i & 1) << (L - 1));
    irt[LIM] = rt[LIM] = 1; int Wn = q_pow(Gen, (MOD - 1) / LIM), iWn = inv(Wn);
    for (ri i = 1; i < (LIM >> 1); ++i) rt[i | LIM] = 1ll * rt[i - 1 | LIM] * Wn % MOD, irt[i | LIM] = 1ll * irt[i - 1 | LIM] * iWn % MOD;
    LIM >>= 1, --L;
    fac[0] = ifac[0] = fac[1] = ifac[1] = I[1] = 1;
    for (ri i = 2; i <= LIM; ++i) {
        fac[i] = 1ll * fac[i - 1] * i % MOD, I[i] = 1ll * (MOD - MOD / i) * I[MOD % i] % MOD, ifac[i] = 1ll * ifac[i - 1] * I[i] % MOD;
    }	for (int i = 0; i < D; ++i) Dec(b[a[i]], a[i + 1]);
	for (int i = 0; i < LIM; ++i) if (b[i]) {
		for (int j = 1; j * i < LIM; ++j) Add(F[j * i], 1ll * b[i] * I[j] % MOD);
	}
	Exp(F, G, LIM, L);
	if (N % 2 == 0) {
		for (int i = 0; i < LIM; ++i) G[i] = dec(0, G[i]);
	}
	for (int i = 0; i < LIM; ++i) H[i] = F[i] = 0;
	for (int i = 0; i <= S; ++i) H[S - i] = 1ll * G[i] * fac[i] % MOD;
	for (int i = 0; i <= S; ++i) F[i] = ifac[i];
	Multiply(H, F, LIM << 1, L + 1);
	for (int i = 0; i <= S; ++i) F[i] = 1ll * H[S - i] * ifac[i] % MOD;
	for (int i = S + 1; i < LIM; ++i) F[i] = 0;
	int sum = 1, tmp = 0;
	for (int i = 0; i <= D; ++i) Add(tmp, 1ll * a[i] * dec(a[i], 1) % MOD * I[2] % MOD);
	for (int i = 0; i < LIM; ++i) G[i] = 0;
	for (int i = 0; i <= max(M, S); ++i) G[i] = 1ll * ifac[i] * sum % MOD, sum = 1ll * sum * tmp % MOD, Dec(tmp, 1);
	Multiply(F, G, LIM << 1, L + 1);
	printf("%d\n", F[M]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值