[TJOI2019]唱、跳、rap和篮球(指数型生成函数+NTT+卷积)
文章目錄
- 題目
- 題解
- code1(NTT)
- code2(EGF+卷積)
題目
大中鋒的學(xué)院要組織學(xué)生參觀博物館,要求學(xué)生們?cè)诓┪镳^中排成一隊(duì)進(jìn)行參觀。他的同學(xué)可以分為四類:一部分最喜歡唱、一部分最喜歡跳、一部分最喜歡rap,還有一部分最喜歡籃球。如果隊(duì)列中k,k+1,k+2,k+3k,k + 1,k + 2,k + 3k,k+1,k+2,k+3位置上的同學(xué)依次,最喜歡唱、最喜歡跳、最喜歡rap、最喜歡籃球,那么他們就會(huì)聚在一起討論蔡徐坤。大中鋒不希望這種事情發(fā)生,因?yàn)檫@會(huì)使得隊(duì)伍顯得很亂。大中鋒想知道有多少種排隊(duì)的方法,不會(huì)有學(xué)生聚在一起討論蔡徐坤。兩個(gè)學(xué)生隊(duì)伍被認(rèn)為是不同的,當(dāng)且僅當(dāng)兩個(gè)隊(duì)伍中至少有一個(gè)位置上的學(xué)生的喜好不同。由于合法的隊(duì)伍可能會(huì)有很多種,種類數(shù)對(duì)998244353取模。
輸入格式
輸入數(shù)據(jù)只有一行。每行5個(gè)整數(shù),第一個(gè)整數(shù)n,代表大中鋒的學(xué)院要組織多少人去參觀博物館。接下來(lái)四個(gè)整數(shù)a、b、c、d,分別代表學(xué)生中最喜歡唱的人數(shù)、最喜歡跳的人數(shù)、最喜歡rap的人數(shù)和最喜歡籃球的人數(shù)。保證a+b+c+d≥na+b+c+d \ge na+b+c+d≥n。
輸出格式
每組數(shù)據(jù)輸出一個(gè)整數(shù),代表你可以安排出多少種不同的學(xué)生隊(duì)伍,使得隊(duì)伍中沒有學(xué)生聚在一起討論蔡徐坤。結(jié)果對(duì)998244353998244353998244353取模。
輸入輸出樣例
輸入
4 4 3 2 1
輸出
174
輸入
996 208 221 132 442
輸出
442572391
說(shuō)明/提示
對(duì)于20%的數(shù)據(jù),有n=a=b=c=d≤500n=a=b=c=d\le500n=a=b=c=d≤500
對(duì)于100%的數(shù)據(jù),有n≤1000n \le 1000n≤1000 , a,b,c,d≤500a, b, c, d \le 500a,b,c,d≤500
題解
考慮枚舉有iii堆人在討論cxk小姐姐,那么被cxk迷倒的人就有4i4i4i個(gè),且每一堆人是挨在一起的,
所以我們可以把這4i4i4i個(gè)人縮成iii個(gè)群體,每一個(gè)群體可以展開成為444人
那么現(xiàn)在就只用考慮剩下的n?3in-3in?3i人,放置的方案數(shù)就是Cn?3iiC_{n-3i}^iCn?3ii?
簡(jiǎn)單用整體法證明一下:
在n?3in-3in?3i人中,要選iii個(gè)拿來(lái)討論,n?4in-4in?4i個(gè)不討論,方案數(shù)是對(duì)應(yīng)的Cn?3ii=Cn?3in?4iC_{n-3i}^i=C_{n-3i}^{n-4i}Cn?3ii?=Cn?3in?4i?
也可以理解為在n?3in-3in?3i個(gè)空格里面找iii個(gè)起點(diǎn)開始討論小姐姐
接著枚舉好了iii及它們可能出現(xiàn)的地方Cn?3iiC_{n-3i}^iCn?3ii?,我們就要去考慮剩下n?4in-4in?4i人的排列,可以亂來(lái)
(n?4i)!(n-4i)!(n?4i)!
但是attentionattentionattention,如果看了我的上一篇指數(shù)型生成函數(shù)專練,就會(huì)與這里有一點(diǎn)相通
題目說(shuō)的方案數(shù)不同的要求是至少有一個(gè)位置上的學(xué)生的喜好不同,而不是學(xué)生本人不同
所以我們要除掉喜好籃球,跳舞,唱歌,rap本身內(nèi)部的亂拍,因?yàn)閺膼酆蒙蟻?lái)看是看不出來(lái)排列不同的
設(shè)a,b,c,da,b,c,da,b,c,d分別代表學(xué)生中最喜歡唱的人數(shù)、最喜歡跳的人數(shù)、最喜歡rap的人數(shù)和最喜歡籃球的總?cè)藬?shù),我們考慮的是剩下的不討論cxk姐姐的學(xué)生亂排,所以要剪掉外層所枚舉的去參與討論的人數(shù)
(n?4i)!(a?i)!(n?i)!(c?i)!(d?i)!\frac{(n-4i)!}{(a-i)!(n-i)!(c-i)!(d-i)!}(a?i)!(n?i)!(c?i)!(d?i)!(n?4i)!?
最后寫出每種喜好的生成函數(shù),以喜歡籃球的人為例:
∑i=0cxii!\sum_{i=0}^{c}\frac{x^i}{i!}i=0∑c?i!xi?
把四種愛好卷起來(lái),卷出最后乘積的第n?4in-4in?4i項(xiàng)就是我們需要除掉的東西,乘上(n?4i)!(n-4i)!(n?4i)!就是真正的亂排數(shù)
因?yàn)閹∧?#xff0c;所以是用NTTNTTNTT跑,除的話就要變成乘逆元,所以我們可以在卷的時(shí)候就直接卷逆元
但是我們又發(fā)現(xiàn)雖然假設(shè)的是iii堆人在討論,但是統(tǒng)計(jì)答案的時(shí)候卻把≥i\ge i≥i堆人討論的情況都統(tǒng)計(jì)了
而且當(dāng)統(tǒng)計(jì)i=1i=1i=1時(shí),會(huì)算兩遍至少兩堆人討論的方案,三遍至少三堆人討論的方案…在統(tǒng)計(jì)i=2i=2i=2時(shí),會(huì)算三遍至少三堆人討論的方案…
當(dāng)統(tǒng)計(jì)iii的方案時(shí),會(huì)多算CjiC_j^iCji?次至少jjj堆人討論的方案,所以我們可以用容斥來(lái)解決
總結(jié)一下答案應(yīng)該是
∑i=0min(?1)i?Cn?3ii?(n?4i)!(a?i)!(n?i)!(c?i)!(d?i)!\sum_{i=0}^{min}(-1)^i*C_{n-3i}^i*\frac{(n-4i)!}{(a-i)!(n-i)!(c-i)!(d-i)!}i=0∑min?(?1)i?Cn?3ii??(a?i)!(n?i)!(c?i)!(d?i)!(n?4i)!?
code1(NTT)
#include <cstdio> #include <iostream> using namespace std; #define mod 998244353 #define LL long long #define MAXN 10005 int n, anum, bnum, cnum, dnum; LL pi, ni, result; LL a[MAXN], b[MAXN], c[MAXN], d[MAXN], rev[MAXN], fac[MAXN], Invfac[MAXN];LL qkpow ( LL x, LL y ) {LL ans = 1;while ( y ) {if ( y & 1 )ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }LL C ( int n, int m ) {return fac[n] * Invfac[m] % mod * Invfac[n - m] % mod; }void NTT ( LL *c, LL limit, LL f ) {for ( LL i = 0;i < limit;i ++ )if ( i < rev[i] )swap ( c[i], c[rev[i]] );for ( LL i = 1;i < limit;i <<= 1 ) {LL omega = qkpow ( f == 1 ? pi : ni, ( mod - 1 ) / ( i << 1 ) );for ( LL j = 0;j < limit;j += ( i << 1 ) ) {LL w = 1;for ( LL k = 0;k < i;k ++, w = w * omega % mod ) {LL x = c[k + j], y = w * c[i + j + k] % mod;c[k + j] = ( x + y ) % mod;c[k + j + i] = ( x - y + mod ) % mod;}}}LL inv = qkpow ( limit, mod - 2 );if ( f == -1 )for ( LL i = 0;i < limit;i ++ )c[i] = c[i] * inv % mod; }void init () {Invfac[0] = fac[0] = 1;for ( int i = 1;i <= n;i ++ )fac[i] = fac[i - 1] * i % mod;Invfac[n] = qkpow ( fac[n], mod - 2 );for ( int i = n - 1;i;i -- )Invfac[i] = Invfac[i + 1] * ( i + 1 ) % mod; }LL solve ( int n, int A, int B, int C, int D ) {LL len = 1, l = 0;while ( len < ( ( A + B + C + D ) << 1 ) ) {len <<= 1;l ++;}for ( LL i = 0;i < len;i ++ )rev[i] = ( rev[i >> 1] >> 1 ) | ( ( i & 1 ) << ( l - 1 ) );for ( int i = 0;i < len;i ++ )a[i] = ( i <= A ? Invfac[i] : 0 );for ( int i = 0;i < len;i ++ )b[i] = ( i <= B ? Invfac[i] : 0 );for ( int i = 0;i < len;i ++ )c[i] = ( i <= C ? Invfac[i] : 0 );for ( int i = 0;i < len;i ++ )d[i] = ( i <= D ? Invfac[i] : 0 );NTT ( a, len, 1 );NTT ( b, len, 1 );NTT ( c, len, 1 );NTT ( d, len, 1 );for ( int i = 0;i < len;i ++ )a[i] = a[i] * b[i] % mod * c[i] % mod * d[i] % mod;NTT ( a, len, -1 );return a[n] * fac[n] % mod; }int main() {pi = 3;ni = mod / pi + 1;scanf ( "%d %d %d %d %d", &n, &anum, &bnum, &cnum, &dnum );init();int k = min ( min ( min ( min ( anum, bnum ), cnum ), dnum ), n / 4 );anum -= k;bnum -= k;cnum -= k;dnum -= k;result = 0;for ( k;k >= 0;k -- ) {LL tmp = C ( n - 3 * k, k ) % mod * solve ( n - 4 * k, anum, bnum, cnum, dnum ) % mod;anum ++;bnum ++;cnum ++;dnum ++;result = ( result + ( ( k & 1 ) ? mod - tmp : tmp ) ) % mod;}printf ( "%lld\n", result );return 0; }code2(EGF+卷積)
#include <cstdio> #include <iostream> using namespace std; #define mod 998244353 #define LL long long #define MAXN 1005 int n, a, b, c, d; LL result; LL foldAB[MAXN], foldCD[MAXN], fac[MAXN], Invfac[MAXN];LL qkpow ( LL x, LL y ) {LL ans = 1;while ( y ) {if ( y & 1 )ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }LL C ( int n, int m ) {return fac[n] * Invfac[m] % mod * Invfac[n - m] % mod; }void Fac () {Invfac[0] = fac[0] = 1;for ( int i = 1;i <= n;i ++ )fac[i] = fac[i - 1] * i % mod;Invfac[n] = qkpow ( fac[n], mod - 2 );for ( int i = n - 1;i;i -- )Invfac[i] = Invfac[i + 1] * ( i + 1 ) % mod; }int main() {scanf ( "%d %d %d %d %d", &n, &a, &b, &c, &d );Fac();int k = min ( min ( min ( min ( a, b ), c ), d), n / 4 );a -= k;b -= k;c -= k;d -= k;for ( int i = 0;i <= a;i ++ )for ( int j = 0;j <= b;j ++ )foldAB[i + j] = ( foldAB[i + j] + Invfac[i] * Invfac[j] % mod ) % mod;for ( int i = 0;i <= c;i ++ )for ( int j = 0;j <= d;j ++ )foldCD[i + j] = ( foldCD[i + j] + Invfac[i] * Invfac[j] % mod ) % mod;result = 0;for ( k;k >= 0;k -- ) {LL ans = 0;int tp = n - 4 * k;for ( int i = 0;i <= tp;i ++ )ans = ( ans + foldAB[i] * foldCD[tp - i] % mod ) % mod;ans = ans * fac[tp] % mod * C ( n - k * 3, k ) % mod;result = ( result + ( ( k & 1 ) ? mod - ans : ans ) ) % mod;a ++;b ++;c ++;d ++;for ( int i = 0;i <= a;i ++ )foldAB[i + b] = ( foldAB[i + b] + Invfac[i] * Invfac[b] % mod ) % mod;for ( int i = 0;i <= b;i ++ )foldAB[i + a] = ( foldAB[i + a] + Invfac[i] * Invfac[a] % mod ) % mod;foldAB[a + b] = ( foldAB[a + b] - Invfac[a] * Invfac[b] % mod + mod ) % mod;for ( int i = 0;i <= c;i ++ )foldCD[i + d] = ( foldCD[i + d] + Invfac[i] * Invfac[d] % mod ) % mod;for ( int i = 0;i <= d;i ++ )foldCD[i + c] = ( foldCD[i + c] + Invfac[i] * Invfac[c] % mod ) % mod;foldCD[c + d] = ( foldCD[c + d] - Invfac[c] * Invfac[d] % mod ) % mod;}printf ( "%lld\n", result );return 0; }代碼或者思路有任何問題歡迎評(píng)論
總結(jié)
以上是生活随笔為你收集整理的[TJOI2019]唱、跳、rap和篮球(指数型生成函数+NTT+卷积)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 江米粽子的做法
- 下一篇: [矩阵乘法/快速幂专题]Arc of D