zoukankan      html  css  js  c++  java
  • Solution -「CTS2019」珍珠

    题目

      luogu.

    题解

      先 % 兔。同为兔子为什么小粉兔辣么强qwq。 本文大体跟随小粉兔的题解的思路,并为像我一样多项式超 poor 的读者作了很详细的解释。如果题解界面公式出现问题,可以尝试“在 Ta 的博客查看”w~

      生成函数 + NTT。

      首先,转化题意:求长度为 (n),元素属于 ([1,D]) 且存在至少 (m) 对位置不重复的相同元素的整数序列个数。

      不妨把元素的值形象化为颜色,设第 (c) 中颜色在某个序列中出现次数为 (cnt_c)。则该序列合法,当且仅当:

    [sum_{c=1}^Dlfloorfrac{cnt_c}2 floorge m ag1 ]

      也即是,每种颜色最多组合出 (lfloorfrac{cnt_c}2 floor) 组相同元素对,并将其求和。对 ((1)) 变形:

    [(1)Rightarrowsum_{c=1}^Dfrac{cnt_c-(cnt_cmod 2)}2ge m ]

    [Rightarrowsum_{c=1}^Dcnt_c-(cnt_cmod2)ge2m ]

    [Rightarrow n-sum_{c=1}^Dcnt_cmod 2ge2m ]

    [Rightarrowsum_{c=1}^Dcnt_cmod2le n-2m ag2 ]

      由 ((2)),产生了一些特判:当 (n<2m),非负数之和不可能小于 (0),答案为 (0);当 (Dle n-2m),等式恒成立(颜色总数仅 (D)),答案为 (D^n)

      接下来,令 (g_i) 表示恰好(i)(cnt_cmod2=1) 的方案数。即 (g_i=sum_{cnt}[sum_{c=1}^Dcnt_cmod2=i])。可以发现最终答案为 (sum_{c=0}^{n-2m}g_c),即 ((2)) 的等式左边所有满足条件的取值的方案数之和。

      然后容斥 (g):令 (f_i)至少(i)(cnt_cmod2=1) 的方案数。注意:(f) 考虑了与 (g) 的容斥关系。更细致地阐释,(f_i) 表示先保证有 (i) 个颜色为奇数的前提下,其它颜色任意选的方案总数。如此一来,对于任意 (ige j)(g_i) 会对 (f_j) 贡献 (ichoose j) 次——在 (i) 中选 (j) 个“保底”,其余 (i-j) 个成为“任意选”方案中的若干种。则:

    [f_i=sum_{j}{jchoose i}g_j ag3 ]

      利用二项式反演,对 ((3)) 变形:

    [g_i=sum_j(-1)^{j-i}{jchoose i}f_j=sum_j(-1)^{i-j}frac{j!}{i!(j-i)!}f_j=frac{1}{i!}sum_jfrac{(-1)^{i-j}}{[-(i-j)]!}cdot j!f_j ag4 ]

      可以发现,在最后一个式子的求和中,点乘号左右两边分别是关于 (j)(i-j) 的函数,那么 (g) 实际上就是一个卷积的形式。只要求出 (f) 即可。


      回顾 (f) 的定义,并尝试引入指数型生成函数。考虑到对于序列 (C={1,1,1,dots}),其生成函数为 (G_e(C)=sum_ifrac{x^i}{i!}=e^x);对于序列 (O={0,1,0,1,dots}),其生成函数为 (G_e(O)=frac{x_1}{1!}+frac{x_3}{3!}+frac{x_5}{5!}+cdots=frac{e^x-e^{-x}}2)。同时,记 ([x^n]f(x)) 表示函数 (f(x))(n) 次项系数,则有:

    [f_i={Dchoose i}n![x^n]left(G_e^i(O)G_e^{D-i}(C) ight) ag5 ]

      先停下来理解:

    • 第一项 (Dchoose i),先钦定 (D) 种颜色中的 (i) 个为必须奇数。别忘了 (f) 定义,其余颜色也可以为奇数,它们是任意的。

    • 第二项 (n!),是约掉指数生成函数第 (n) 项的 (frac{1}{n!})。比如 ([x^3]G_e(C)=frac{1}{3!}),需要乘上 (frac{1}{3!}) 才为 (1)

    • 第三项 ([x^n]left(G_e^i(O)G_e^{D-i}(C) ight)),正如一般的卷积,生成函数的卷积表现了在不同集合中选择的方案。该式中卷积的第 (n) 项就表示选择 (i) 个奇数,再任选 (D-i) 个非负数,使得其和恰为 (n) 的方案数。可以结合最初的题意理解。

      理解之后,着手对 ((5)) 变形(注意所有的 (i) 均为下标,而非虚数单位):

    [f_i={Dchoose i}n![x^n]left(frac{(e^x-e^{-x})^i}{2^i}cdot e^{x(D-i)} ight) ]

    [Rightarrow f_i={frac{1}{2^i}{Dchoose i}n![x^n]left((e^x-e^{-x})^ie^{x(D-i)} ight)} ag6 ]

      利用二项式定理,展开 ((6)) 中的 ((e^x-e^{-x})^i),得:

    [Rightarrow f_i=frac{1}{2^i}{Dchoose i}n![x^n]left(sum_{j=0}^i{ichoose j}e^{jx}(-e^{-x})^{i-j}e^{x(D-i)} ight) ]

    [Rightarrow f_i=frac{1}{2^i}{Dchoose i}n!sum_{j=0}^i(-1)^{i-j}{ichoose j}[x^n]e^{(D+2j-2i)x} ag7 ]

      发现 (e^{(D+2j-2i)x}) 是形如 (e^{ax}) 的形式,而 (e^{ax}) 是序列 ({a^0,a^1,a^2,dots}) 的生成函数。所以其 (n) 次项应为 (frac{a^n}{n!})。则 ([x^n]e^{(D+2j-2i)x}=frac{(D+2j-2i)^n}{n!})。将其代入 ((7)) 并整理:

    [Rightarrow f_i=frac{1}{2^i}{Dchoose i}n!sum_{j=0}^i(-1)^{i-j}{ichoose j}frac{(D+2j-2i)^n}{n!} ]

    [Rightarrow f_i=frac{i!}{2^i}{Dchoose i}sum_{j=0}^i(-1)^{i-j}frac{(D+2j-2i)^n}{j!(i-j)!} ]

    [Rightarrow f_i=frac{D!}{2^i(D-i)!}sum_{j=0}^i(-1)^{i-j}frac{(D+2j-2i)^n}{j!(i-j)!} ag8 ]

      把 ((8)) 朝着卷积的形式靠近,即分开 (i-j)(j)。为了美观,令 (k=i-j),则 (j=i-k)。推出:

    [Rightarrow f_i=frac{D!}{2^i(D-i)!}sum_{k=0}^ifrac{1}{(i-k)!}cdotfrac{(-1)^k(D-2k)^n}{k!} ag9 ]

      此时,和式已经是卷积的形式。
      到此,利用 NTT,通过 ((9)) 求出 (f),再通过 ((4)) 求出 (g),最后求得答案为 (sum_{c=0}^{n-2m}g_c),并记得特判 ((2)) 所引出的两种情况,这道题就解决啦~

    代码

    #include <cstdio>
    
    const int MOD = 998244353, G = 3, INV2 = ( MOD + 1 ) >> 1, MAXD = 1 << 18;
    int D, n, m, inv[MAXD + 5], fac[MAXD + 5], ifac[MAXD + 5];
    int A[MAXD + 5], B[MAXD + 5], rev[MAXD + 5];
    
    inline int qkpow ( int a, int b, const int p = MOD ) {
    	int ret = 1;
    	for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
    	return ret;
    }
    
    inline void NTT ( const int n, int* a, const int tp ) {
    	for ( int i = 0; i < n; ++ i ) {
    		if ( i < rev[i] ) {
    			a[i] ^= a[rev[i]] ^= a[i] ^= a[rev[i]];
    		}
    	}
    	for ( int i = 2, stp = 1; i <= n; i <<= 1, stp <<= 1 ) {
    		int w = qkpow ( G, ( MOD - 1 ) / i );
    		if ( ! ~ tp ) w = qkpow ( w, MOD - 2 );
    		for ( int j = 0; j < n; j += i ) {
    			for ( int k = j, r = 1; k < j + stp; ++ k, r = 1ll * r * w % MOD ) {
    				int ev = a[k], ov = 1ll * a[k + stp] * r % MOD;
    				a[k] = ( ev + ov ) % MOD, a[k + stp] = ( ev - ov + MOD ) % MOD;
    			}
    		}
    	}
    	if ( ! ~ tp ) {
    		int rvn = qkpow ( n, MOD - 2 );
    		for ( int i = 0; i < n; ++ i ) a[i] = 1ll * a[i] * rvn % MOD;
    	}
    }
    
    inline void polyConv ( int lenA, int lenB, int* A, int* B, int* C ) {
    	int lenC = lenA + lenB - 1, len = 1, lg = 0;
    	for ( ; len < lenC; len <<= 1, ++ lg );
    	for ( int i = 0; i < len; ++ i ) rev[i] = ( rev[i >> 1] >> 1 ) | ( ( i & 1 ) << lg >> 1 );
    	static int tmpA[MAXD + 5], tmpB[MAXD + 5];
    	for ( int i = 0; i < len; ++ i ) {
    		tmpA[i] = i < lenA ? A[i] : 0;
    		tmpB[i] = i < lenB ? B[i] : 0;
    	}
    	NTT ( len, tmpA, 1 ), NTT ( len, tmpB, 1 );
    	for ( int i = 0; i < len; ++ i ) C[i] = 1ll * tmpA[i] * tmpB[i] % MOD;
    	NTT ( len, C, -1 );
    }
    
    inline void init () {
    	inv[1] = fac[1] = ifac[1] = fac[0] = ifac[0] = 1;
    	for ( int i = 2; i <= D; ++ i ) {
    		fac[i] = 1ll * i * fac[i - 1] % MOD;
    		inv[i] = 1ll * ( MOD - MOD / i ) * inv[MOD % i] % MOD;
    		ifac[i] = 1ll * inv[i] * ifac[i - 1] % MOD;
    	}
    }
    
    int main () {
    	scanf ( "%d %d %d", &D, &n, &m );
    	if ( m << 1 > n ) return puts ( "0" ), 0;
    	if ( m << 1 <= n - D ) return printf ( "%d
    ", qkpow ( D, n ) ), 0;
    	init ();
    	for ( int i = 0; i <= D; ++ i ) {
    		A[i] = 1ll * qkpow ( ( D - 2 * i + MOD ) % MOD, n ) * ( i & 1 ? MOD - ifac[i] : ifac[i] ) % MOD;
    		B[i] = ifac[i];
    	}
    	polyConv ( D + 1, D + 1, A, B, A );
    	for ( int i = 0; i <= D; ++ i ) {
    		A[i] = 1ll * A[i] * fac[D] % MOD * fac[i] % MOD * qkpow ( INV2, i ) % MOD * ifac[D - i] % MOD;
    		B[D - i] = i & 1 ? MOD - ifac[i] : ifac[i];
    	}
    	polyConv ( D + 1, D + 1, A, B, A );
    	int ans = 0;
    	for ( int i = 0; i <= n - 2 * m; ++ i ) ans = ( ans + 1ll * ifac[i] * A[D + i] ) % MOD;
    	printf ( "%d
    ", ans );
    	return 0;
    }
    
  • 相关阅读:
    HDU 1358 Period (KMP)
    POJ 1042 Gone Fishing
    Csharp,Javascript 获取显示器的大小的几种方式
    css text 自动换行的实现方法 Internet Explorer,Firefox,Opera,Safar
    Dynamic Fonts动态设置字体大小存入Cookie
    CSS Image Rollovers翻转效果Image Sprites图片精灵
    CSS three column layout
    css 自定义字体 Internet Explorer,Firefox,Opera,Safari
    颜色选择器 Color Picker,Internet Explorer,Firefox,Opera,Safar
    CSS TextShadow in Safari, Opera, Firefox and more
  • 原文地址:https://www.cnblogs.com/rainybunny/p/13089258.html
Copyright © 2011-2022 走看看