数论三之排列组合Ⅱ——Virus Tree 2,RGB Coloring,123 Triangle,排列计数,排队,卡农
絲且人一口
- Virus Tree 2
- description
- solution
- code
- RGB Coloring
- description
- solution
- code
- 123 Triangle
- description
- solution
- code
- [SDOI2016]排列計數
- description
- solution
- code
- [HNOI2012]排隊
- description
- solution
- code
- [HNOI2011]卡農
- description
- solution
- code
Virus Tree 2
description
solution
距離不超過222的點,可能是兒子/孫子/父親/爺爺
考慮從上到下,由上面的顏色選取決定下面的顏色
顯然,第一個點有KKK種選擇,第二個點有K?1K-1K?1種,每次不同都要?1-1?1
答案就是每個點的選擇的乘積
所以只需要把這種過程的遞減通過dfs樹來實現
對于現在的子樹根節點uuu,如果vvv是第一個兒子,那么需要考慮vvv有沒有爺爺
如果不是第一個兒子,那么就是前一個兒子的方案數再?1-1?1
code
#include <cstdio> #include <vector> using namespace std; #define mod 1000000007 #define int long long #define maxn 1000005 vector < int > G[maxn]; int n, k; int w[maxn];void dfs( int u, int fa ) {int r = 0;for( auto v : G[u] ) {if( v == fa ) continue;else {if( r ) w[v] = r - 1;else if( ! fa ) w[v] = k - 1;else w[v] = k - 2;r = w[v]; }}for( auto v : G[u] ) if( v ^ fa ) dfs( v, u ); }signed main() {scanf( "%lld %lld", &n, &k );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u );}w[1] = k;dfs( 1, 0 );int ans = 1;for( int i = 1;i <= n;i ++ )if( w[i] <= 0 ) return ! printf( "0\n" );else ans = ans * w[i] % mod;printf( "%lld\n", ans );return 0; }RGB Coloring
description
solution
將綠色A+BA+BA+B看成既涂了紅色AAA,又涂了藍色BBB,則紅色和藍色格子彼此獨立,就算涂在同一個格子上也沒關系
枚舉紅色格子的數量iii,計算得出藍色格子數量j=K?A×iBj=\frac{K-A\times i}{B}j=BK?A×i?
判斷格子數量為整數且都在nnn以內即可,然后用組合計數從nnn個格子中選紅/藍色
∑Cni×Cnj\sum C_n^i\times C_n^j∑Cni?×Cnj?
code
#include <cstdio> #define int long long #define mod 998244353 #define maxn 300005 int n, A, B, k; int fac[maxn], inv[maxn];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }void init() {fac[0] = inv[0] = 1;for( int i = 1;i <= n;i ++ )fac[i] = fac[i - 1] * i % mod;inv[n] = qkpow( fac[n], mod - 2 );for( int i = n - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod; }int C( int n, int m ) {if( m < 0 || n < m ) return 0;else return fac[n] * inv[m] % mod * inv[n - m] % mod; }signed main() {scanf( "%lld %lld %lld %lld", &n, &A, &B, &k );init();int ans = 0; for( int i = 0, j;i <= n;i ++ ) {if( ( k - A * i ) % B ) continue;else j = ( k - A * i ) / B;ans = ( ans + C( n, i ) * C( n, j ) ) % mod;}printf( "%lld\n", ans );return 0; }123 Triangle
description
solution
N>1N>1N>1式子是差分形式,后面序列只有能是0/1/20/1/20/1/2
(除非N=1N=1N=1的情況答案有可能是333)
如果此時序列有111,那么答案一定只能是0/10/10/1,因為0/20/20/2碰到111都會變成111
如果序列沒有111,則答案只會是0/20/20/2,對于這種情況,將每個數/2/2/2得到結果后再×2\times 2×2是等價的
所以現在的答案都只會是0/10/10/1,考慮在(mod2)\pmod 2(mod2)意義下做
在(mod2)\pmod 2(mod2)的情況下,加減法可以看成二進制的異或
所求即為一段序列相鄰兩個數異或直到最后成一個數的答案
考慮每個數被異或的次數,這其實是個倒楊輝三角(0,0)(0,0)(0,0)開始
長為nnn的序列的第iii個元素被異或的次數為Cn?1i?1C_{n-1}^{i-1}Cn?1i?1?
計算在模222意義下(除法不一定有逆元)的組合數
- 計算每個階乘中分解222的次數,將除法變成減法,對于組合數最后剩下的222的次數為000,則為1(mod2)1\pmod 21(mod2),反之0(mod2)0\pmod 20(mod2)
- 階乘的分解,拆分成對每個數求出分解中222的次數,做前綴和
code
#include <cmath> #include <cstdio> #define maxn 1000005 int n; char s[maxn]; int x[maxn], cnt[maxn]; int main() {scanf( "%d %s", &n, s + 1 );n --;for( int i = 1;i <= n;i ++ )x[i] = fabs( s[i] - s[i + 1] );bool flag = 1;for( int i = 1;i <= n;i ++ )if( x[i] == 1 ) { flag = 0; break; }if( flag ) for( int i = 1;i <= n;i ++ )x[i] >>= 1;for( int i = 1;i <= n;i ++ ) {int t = i;while( ! ( t & 1 ) ) t >>= 1, cnt[i] ++;cnt[i] += cnt[i - 1];}int ans = 0;for( int i = 1;i <= n;i ++ )ans ^= cnt[n - 1] - cnt[i - 1] - cnt[n - i] ? 0 : ( x[i] & 1 );if( flag ) ans <<= 1;printf( "%d", ans );return 0; }[SDOI2016]排列計數
description
solution
組合數從nnn個中選mmm個強制ai=ia_i=iai?=i,剩下的n?mn-mn?m個ai≠ia_i≠iai??=i,就是n?mn-mn?m的錯排數
code
#include <cstdio> #define mod 1000000007 #define int long long #define maxn 1000005 int fac[maxn], inv[maxn], D[maxn]; int T, n, m;int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }void init() {fac[0] = inv[0] = 1;for( int i = 1;i < maxn;i ++ )fac[i] = fac[i - 1] * i % mod;inv[maxn - 1] = qkpow( fac[maxn - 1], mod - 2 );for( int i = maxn - 2;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod;D[0] = 0, D[1] = 0, D[2] = 1;for( int i = 3;i < maxn;i ++ )D[i] = ( D[i - 1] + D[i - 2] ) * ( i - 1 ) % mod; }int C( int n, int m ) {return fac[n] * inv[m] % mod * inv[n - m] % mod; }signed main() {init();scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld", &n, &m );if( n == m ) printf( "1\n" ); else printf( "%lld\n", C( n, m ) * D[n - m] % mod );}return 0; }[HNOI2012]排隊
description
solution
不給模數還明示答案可能很大,赤裸裸的大整數脅迫!!居心叵測!!
兩名老師真的是很煩了,沒有這么討人厭的老師,可以直接女生插板法了
-
兩名老師之間是男生
先排男生AnnA_n^nAnn?,產生n+1n+1n+1個空
再插板兩名老師,An+12A_{n+1}^2An+12?,產生n+3n+3n+3個空
最后插板mmm名女生,An+3mA_{n+3}^mAn+3m?
-
兩名老師之間是女生
先排男生AnnA_n^nAnn?
再打包兩名老師放同一個隔板里,老師間還有順序,A22Cn+11A_2^2C_{n+1}^1A22?Cn+11?
先強制選一名女生放進老師中間mmm,剩下的就隔板插
男生nnn個,老師和強制女生打包裝成一個,總共是n+1n+1n+1個,產生n+2n+2n+2個空,An+2m?1A_{n+2}^{m-1}An+2m?1?
綜上,ans=AnnAn+12An+3m+A22Cn+11An+2m?1ans=A_n^nA_{n+1}^2A_{n+3}^m+A_2^2C_{n+1}^1A_{n+2}^{m-1}ans=Ann?An+12?An+3m?+A22?Cn+11?An+2m?1?
同樣,老師不相鄰===不考慮老師?-?老師相鄰
-
不考慮老師
把老師當成男的,女的直接插,An+2n+2An+3mA_{n+2}^{n+2}A_{n+3}^mAn+2n+2?An+3m?
-
老師相鄰
捆綁法強制兩名老師一起當成一個男的A22An+1n+1An+2mA_2^2A_{n+1}^{n+1}A_{n+2}^mA22?An+1n+1?An+2m?
二者做差就是答案
code
#include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, m;struct Int {int g[20000], len;Int() {memset( g, 0, sizeof( g ) );len = 0;}Int( int x ) {memset( g, 0, sizeof( g ) );len = 0;if( ! x ) len = 1;else while( x ) g[len ++] = x % 10, x /= 10;}void clean() {while( len > 1 && ! g[len - 1] ) len --;}Int operator - ( Int t ) {Int ans;ans.len = len;for( int i = 0;i < t.len;i ++ ) {ans.g[i] = g[i] - t.g[i];if( ans.g[i] < 0 ) ans.g[i] += 10, g[i + 1] --;}for( int i = t.len;i < len;i ++ )ans.g[i] = g[i]; ans.clean();return ans;}Int operator * ( Int t ) {Int ans;ans.len = len + t.len;for( int i = 0;i < len;i ++ )for( int j = 0;j < t.len;j ++ )ans.g[i + j] += g[i] * t.g[j];for( int i = 0;i < ans.len;i ++ )ans.g[i + 1] += ans.g[i] / 10, ans.g[i] %= 10;ans.len ++;ans.clean();return ans;} void print() {for( int i = len - 1;~ i;i -- )printf( "%d", g[i] );}};Int calc( int n, int m ) {Int ans = ( 1 );for( int i = n - m + 1;i <= n;i ++ )ans = ans * i;return ans; }int main() {scanf( "%d %d", &n, &m );Int ans;ans = calc( n + 2, n + 2 ) * calc( n + 3, m ) - calc( 2, 2 ) * calc( n + 1, n + 1 ) * calc( n + 2, m ); ans.print();return 0; }[HNOI2011]卡農
description
solution
同種音樂其實通過除以m!m!m!就可以消掉
相當于在SSS中選mmm個子集,滿足
- 子集非空
- 不存在兩個完全一樣的子集
- ?i,i∈[1,n]\forall_{i,i\in[1,n]}?i,i∈[1,n]?每個元素出現次數為偶數
設fi:f_i:fi?: 考慮iii個子集,滿足以上所有性質的方案數
- 如果知道i?1i-1i?1個子集的,那么iii個子集的集合就被確定了下來A2n?1i?1A_{2^n-1}^{i-1}A2n?1i?1?
- 如果子集是空,那么去掉這個子集剩下的i?1i-1i?1個子集是合法方案fi?1f_{i-1}fi?1?
- 如果iii子集與jjj子集重復,jjj有i?1i-1i?1種選擇,剔除這兩個子集剩下i?2i-2i?2子集是合法的fi?2f_{i-2}fi?2?,iii子集選擇有2n?1?(i?2)2^n-1-(i-2)2n?1?(i?2)(排除掉剩下i?2i-2i?2個子集,不能與之重復)
最后別忘了m!m!m!
code
#include <cstdio> #define mod 100000007 #define int long long #define maxn 1000005 int n, m; int A[maxn], f[maxn];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }signed main() {scanf( "%lld %lld", &n, &m );f[0] = 1, f[1] = 0;int t = ( qkpow( 2, n ) - 1 + mod ) % mod;A[0] = 1;for( int i = 1;i <= m;i ++ )A[i] = ( A[i - 1] * ( t - i + 1 ) % mod + mod ) % mod;int MS = 1;for( int i = 2;i <= m;i ++ ) {f[i] = ( A[i - 1] - f[i - 1] - f[i - 2] * ( i - 1 ) % mod * ( t - i + 2 ) % mod ) % mod;MS = MS * i % mod;}printf( "%lld\n", ( f[m] + mod ) % mod * qkpow( MS, mod - 2 ) % mod );return 0; }總結
以上是生活随笔為你收集整理的数论三之排列组合Ⅱ——Virus Tree 2,RGB Coloring,123 Triangle,排列计数,排队,卡农的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数论三之组合数学Ⅰ-Max-Min Su
- 下一篇: 数论四之综合训练——Magic Pair