注意觀察題目:$pi>pi/2$。
發現特別像什麼?
二叉堆!
於是就變成了:$n$個堆元素進行排列,滿足堆性質的排列對$p$的取模。(堆性質根據題意為大根堆)
設$f_i$為當前堆首為$i$的堆的排列方案數。為滿足堆性質$p_i$顯然只能取剩下若干數的最大值。
發現$f_i$影響$f_$和$f_$兩個結點。
以$i$為根的堆去掉根節點的結點數為$n-i$個,提前統計出第$i$個結點左子樹的大小$size_i$,則右邊為$n-i-size_i$個結點。
根據前面,可以得到這樣的轉移:$f_i=c(n-i,\ size)\times f_\times f_$。
解釋:從剩下的$n-i$個結點中選$size$個結點做其左子樹,然後對於每個子樹存在$f_$和$f_$中排列,根據乘法原理即得。
正確性:這些數是乙個排列,也就是說任意兩個數一定不相等,可以構成乙個嚴格遞增的序列。挑出的若干數及挑出若干數後仍能構成嚴格遞增的序列,所以可以化成子問題。
邊界:當前根結點為葉節點時,$f_i=1$。
最後答案:$f_1$。
注意:整個$dp$過程是$o(n)$的。但是存在計算組合數,複雜度為$o(n-i)$,$i$為當前根結點。根據遞迴主定理易知為$o(nlogn)$的複雜度。
存在模數,就要提前處理逆元。對於$p$過小還要用$lucas$定理,然而資料水而且我不會所以只寫了逆元。
1 #include 23using
namespace
std;45
#define re register
6#define rep(i, a, b) for (re int i = a; i <= b; ++i)
7#define repd(i, a, b) for (re int i = a; i >= b; --i)
8#define maxx(a, b) a = max(a, b);
9#define minn(a, b) a = min(a, b);
10#define ll long long
11#define inf (1 << 30)
1213 inline int
read()
1920
const
int maxn = 1e6 + 5;21
22int n, p, f[maxn << 1], g[maxn << 1
], inv[maxn];
2324
int c(int n, int
m) 30
31int dfs(int
u) 36
37int dp(int u, int
size)
4243
intmain()
53 dfs(1
);54 printf("
%d", dp(1
, n));
55return0;
56 }
ZJOI2010 排列計數
不是很懂為什麼洛谷上這題標籤有數字dp,於是我被騙來做這道題。不過上一道題也是一樣,其實也是組合數學。題意其實是求1.n1.n 1.n 的排列中有多少個可以構成小根堆。f i f i f i 表示以i ii為根的樹的方案數,l,r l,rl,r是左,右子樹結點個數,那麼有 f i f l f r c...
ZJOI2010 排列計數
求1 n的全排列 p i 的個數,滿足對於 i geq 2 有 p i p 隨手畫個圖就可以發現問題是求大小為 n 的小根堆的個數 由於左右子樹互不影響,直接dp即可,設 dp 表示以 i 為根的小根堆的個數,有 dp i dp dp c size i 1 size 注意本題模數可能小於 n 所以要...
ZJOI2010 排列計數
稱乙個1,2,n的排列p1,p2.pn是magic的,當且僅當2 i n時,pi pi 2.計算1,2,n的排列中有多少是magic的,答案可能很大,只能輸出模p以後的值 輸入檔案的第一行包含兩個整數 n和p,含義如上所述。輸出檔案中僅包含乙個整數,表示計算1,2,的排列中,magic排列的個數模 ...