題解 CQOI2017老C的鍵盤

2022-02-27 13:04:31 字數 1698 閱讀 8209

建議大家還是不要閱讀此文了,因為我覺得這題我的解法實在是又不高效又不優美……只是想要記錄一下,畢竟是除了中國象棋之外自己做出的組合dp第一題~

首先如果做題做得多,比較熟練的話,應該能一眼看出這題所給的資訊正好描述的是一棵二叉樹上父子的大小關係。於是確立乙個狀態 \(f[u][i]\) 表示在 \(u\)  及 \(u\) 的子樹內 \(u\) 排名第 i 名的總方案數。(這個狀態應該還是比較好想,我當時想到這個狀態覺得是可做的就堅持了這個狀態)。那麼就考慮如何通過 \(f[ch1][j], f[ch2][k]\) 即它的兩個兒子來轉移到當前的狀態。

我們可以注意到:由兒子轉移到父親,兩個兒子之間其實是沒有互相關聯的,只要讓它們都滿足與父親的大小限制即可。當排名為 \(j\) 的兒子小與父親的時候,說明父親前面起碼預留出 \(j\) 個空位,而當排名為 \(j\) 的兒子大於父親時,說明父親的排名也不能超過限制使得後面不足 \(size2 - j + 1\) 個空位。設 \(s\) 為右兒子子樹中排名 \( < i \) 的個數,則根據這個思路,就可以得出 \(s\) 的大小範圍。

於是我們有組合數轉移方程 \(ans = f[ch1][j] * f[ch2][k] * c[i - 1][s] * c[size[u] - i][size[ch2] - s]\) ;

**又臭又長,不忍直視……

#include using

namespace

std;

#define maxn 400

#define int long long

#define mod 1000000007

intn, f[maxn][maxn], size[maxn];

intc[maxn][maxn], a[maxn];

intread()

while(c >= '

0' && c <= '

9') x = x * 10 + c - '

0', c =getchar();

return x *k;

}void

get_c()

void dp(int

u) }

else

if(a[ch1] ||a[ch2])

int minn = max(i - 1 -size[ch1], 0ll);

int maxx = min(i - 1 - j, size[ch2] -l2);

for(int s = minn; s <= maxx; s ++)

j = x, k = y, ch1 = p, ch2 =q;

}else}}

}void dfs(int

u)

int ch1 = u * 2, ch2 = u * 2 + 1

; size[u] = size[ch1] + size[ch2] + 1

;

if(ch2 <=n) dp(u);

else

else}}

}signed main()

dfs(

1); int ans = 0

;

for(int i = 1; i <= n; i ++)

ans = (ans + f[1][i]) %mod;

printf(

"%lld\n

", ans);

return0;

}

CQOI2017 老C的鍵盤

發現題目給的很像一棵樹。就把這棵樹建出來。發現如果把大於小於號分別看成一條有向邊,發現這個題目就是求這個圖有多少個拓撲序。對於每乙個拓撲序,直接 12345 這樣標號就可以得到滿足題目要求的序列。考慮樹 dp 設 f i,j 為 i 這個點在這個子樹所形成的拓撲序列中在第 j 位的方案數。轉移的時候...

CQOI2017 老C的鍵盤

一句話題意 給你一棵完全二叉樹,每條邊有乙個方向,求這棵樹有多少種不同的拓撲序。簡化題意後,其實就是乙個普及組樹形 dp。設 dp i,j 表示以點 i 為根的子樹中,i 號點排第 j 名的方案數。利用 j 這個輔助維,我們可以列舉點 i 的排名 k 掃一遍點 i 的所有兒子,每次會新來乙個以 v ...

CQOI2017 老C的方塊

傳送門 nkoj4042 這是一道網路流題。我在這道題上耗了很久 感謝 oblack幫我修改 這道題的思維上的難度並不大。以下是我的想法 首先要確定乙個大的方向,題目中要求移除一些方塊使得剩餘方塊無法構成 討厭的形狀 很容易就想到了最小割。仔細觀察一下,可以發現這些討厭的形狀是由兩個方格 一條特殊的...