先特判掉 \(m>n\) 的情況。
考慮先確定乙個 \(f\) 陣列,然後判定他能否生成乙個好序列。
考慮先確定最靠左邊的最大值的位置,此時他大於等於後面的元素,然後嚴格大於前面的元素。
不難發現這種確定方式可以固定一組 \(f\) 序列,同時,如果這張圖上的最長鏈小於等於 \(m\),那麼這個答案就是合法的。
觀察到我們其實只關心長度,於是我們不難得到 \(\mathcal o(n^2m)\) 的暴力做法。
具體來說,令 \(f_\) 表示長度為 \(i\) 的序列,最大值為 \(j\) 的方案數,令 \(s_\) 為其字首和:
具體來說,我們發現這個實際上是乙個樹計數的過程,我們考慮一棵二叉樹,左兒子的邊為藍色,右兒子的邊為紅色,一棵樹合法當且僅當其不存在乙個點走到根節點需要經過 \(m\) 條藍邊。
這樣的話我們考慮直接對 \(s\) 序列進行計數,方便起見我們認為初始的答案為 \(1\),同時規定 \(s_=1\),不難發現:
邊界為 \(s_=s_=1\),答案為 \(s_\)
這個式子看上去就非常的卷積,我們從小到大列舉 \(j\),記 \(f_j(x)\) 表示其生成函式 \(\sum s_x^i\)
不難發現:\(f_j(x)=f_j(x)f_(x)x+1\) 於是得到 \(f_j(x)=\frac(x)x}\)
考慮一類函式復合,\(g(f(x))=\frac\),我們希望求得是 \(g(g(...g(\frac)))\)
基於觀察,我們發現答案一定可以寫成 \(\frac\)
這樣的話遞推形式即為 \(\fracx}=\frac\)
可以寫成矩陣的形式,通過矩陣快速冪 +ntt 處理即可,複雜度 \(\mathcal o(n\log^2 n)\)
最後還要多項式求逆一下。
有沒有更 easy 的做法呢?當然是有的!
注意到,這個題只需要我們算出其中一項 \(f_m[x^n]\),所以我們可以考慮一下奇怪的trick:
我們希望計算二叉樹的數量,可以考慮計算括號序列的數量,根據此題模型,我們可以考慮乙個這樣的轉換:
然後基於這樣的觀察我們發現任意的乙個括號序列都是合法的,將左括號設為 \(+1\),右括號設為 \(-1\) 問題等價於配對的括號序列使得不存在字首和大於 \(m\)(此時假設字首和為 \(x\) 等價於往 \(l\) 子樹走了 \(x-1\) 步)這樣的話我們給 \(m+1\),然後就是大於等於 \(m\) 了。
套路,不妨從座標系的角度考慮( \(y\) 表示左括號,\(x\) 表示右括號)我們等價於計算有多少條路徑,滿足不經過 \(y=x-1\) 和 \(y=x+m\),求走到 \((n,n)\) 的方案數。
這樣的話,仿照 [jloi2015]騙我呢 進行處理即可,複雜度 \(\mathcal o(n)\)
答案等於總方案數減去至少經過了 a,至少經過了 b,加上至少經過了 ab,至少經過了 ba,減去 ... 依次類推。
預處理組合數即可。
\(code:\)
#includeusing namespace std ;
#define next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int long long
int gi()
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}const int p = 998244353 ;
const int n = 2e5 + 5 ;
int n, m, ans, maxn, fac[n], inv[n] ;
struct node
} ed ;
int fpow(int x, int k) return ans ;
}int c(int x, int y)
int operator - (node a, node b)
node f(node x, int b)
int get(node x, int type)
signed main()
fac[0] = inv[0] = 1, ++ m ;
rep( i, 1, maxn )
fac[i] = fac[i - 1] * i % p, inv[i] = fpow(fac[i], p - 2) ;
ans = c(n + n, n) ; ed = node(n, n) ;
ans = ans - get(f(node(0, 0), -1), 0) ;
ans = ans - get(f(node(0, 0), m), 1) ;
ans %= p, ans = (ans + p) % p ;
cout << ans << endl ;
return 0 ;
}
UOJ424 集訓隊作業2018 count
將序列對應到笛卡爾樹,發現每棵笛卡爾樹只對應一種合法序列。因為在笛卡爾樹上往左走其對應的數至少減 1 往右走不一定減 1 所以這棵笛卡爾樹從根節點往左走的次數要 leqslant m 題目就轉化為了統計有多少棵 n 個節點的合法笛卡爾樹。笛卡爾樹是二叉樹,因為二叉樹和括號序列都可以用卡特蘭數計數,所...
UOJ449 集訓隊作業2018 喂鴿子
uoj 看題後 感覺自己越來越菜了,再這樣下去,要是正式考試送溫暖豈不是連溫暖都拿不到了。一臉min max反演的樣子,由於每個鴿子都等價,列舉子集大小 i ii 即可 a ns i 1n n i 1 i 1nif i ans sum n binom n i 1 frac n i f i ans i...
UOJ 449 集訓隊作業2018 喂鴿子
449.集訓隊作業2018 喂鴿子 dp好題 處理前m個,最快吃飽的鴿子期望的時間 根據期望的定義 考慮每個方案數的概率 期望次數 列舉前m個用了x個,概率都是 1 m x em x 而em x 表示往前m個扔了x個期望的總共次數,就是x n m 考慮用了x個的方案數 生成函式egf思想。而出現乙個...