[BZOJ3167][Heoi2013]Sao
阿新 • • 發佈:2017-10-04
賦值 next sum void code n) sam add sta
第一行,一個整數T,表示數據組數。對於每組數據,第一行一個整數n,表示關卡數。接下來n–1行,每行為“i
sign j”,其中0≤i,j≤n–1且i≠j,sign為“<”或者“>”,表示第i個關卡必須在第j個關卡前/後完成。
T≤5,1≤n≤1000
10
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7
10
6 > 7
2 > 0
9 < 0
5 > 9
7 > 0
0 > 3
7 < 8
1 < 2
0 < 4
10
2 < 0
1 > 4
0 > 5
9 < 0
9 > 3
1 < 2
4 > 6
9 < 8
7 > 1
10
0 > 9
5 > 6
3 > 6
8 < 7
8 > 4
0 > 6
8 > 5
8 < 2
1 > 8
10
8 < 3
8 < 4
1 > 3
1 < 9
3 < 7
2 < 8
5 > 2
5 < 6
0 < 9
3960
1834
5208
3336 可以發現把關系看成邊的話這是一顆樹 設$f[i][j]$表示在以$i$為根的子數中,根節點是第$j$大 那麽考慮每次和一顆子樹的答案合並(顯然合並子樹的順序不影響答案) 對於每一個$f[u][j]$來說,當它在合並時孩子$v$時 先不考慮$u$和$v$兩顆樹本身的以及$u$和$v$之間的大小關系,那麽可以 枚舉子樹中前$k$小的點比根小,那麽這一步就有$C_{j+k-1}^{k}*C_{siz[u]+siz[v]-j-k}^{siz[v]-k}$種方案 這是因為把一個子樹從小到大攤成一個序列後 $u$前面有$j+k-1$個位置放點,其中選$k$個給$v$,$u$後面有&siz[u]+siz[v]-j-k&個位置放點,其中選$siz[v]-k$個給孩子
然後要考慮$u$和$v$兩顆子樹本身之間的關系
以$u$在$v$後面為例
$v$肯定在插入$u$之前的那$k$個點裏,方案數共有$\sigma_{x=1}^{k}f[v][x]$種
然後$u$本身有$f[u][j]$種方案
根據乘法原理,把所有方案數乘起來,加到$f[u][j+k]$裏
註意在合並完一個孩子之前要把開輔助數組記錄答案然後再賦值到$f$數組裏
具體看代碼
3167: [Heoi2013]Sao
Time Limit: 30 Sec Memory Limit: 256 MB Submit: 188 Solved: 83 [Submit][Status][Discuss]Description
WelcometoSAO(StrangeandAbnormalOnline)。這是一個VRMMORPG,含有n個關卡。但是,挑戰不同關卡的順序是一 個很大的問題。有n–1個對於挑戰關卡的限制,諸如第i個關卡必須在第j個關卡前挑戰,或者完成了第k個關卡才 能挑戰第l個關卡。並且,如果不考慮限制的方向性,那麽在這n–1個限制的情況下,任何兩個關卡都存在某種程 度的關聯性。即,我們不能把所有關卡分成兩個非空且不相交的子集,使得這兩個子集之間沒有任何限制。
Input
Output
對於每個數據,輸出一行一個整數,為攻克關卡的順序方案個數,mod1,000,000,007輸出。
Sample Input
510
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7
10
6 > 7
2 > 0
9 < 0
5 > 9
7 > 0
0 > 3
7 < 8
1 < 2
0 < 4
10
2 < 0
1 > 4
0 > 5
9 < 0
9 > 3
1 < 2
4 > 6
9 < 8
7 > 1
10
0 > 9
5 > 6
3 > 6
8 < 7
8 > 4
0 > 6
8 > 5
8 < 2
1 > 8
10
8 < 3
8 < 4
1 > 3
1 < 9
3 < 7
2 < 8
5 > 2
5 < 6
0 < 9
Sample Output
25803960
1834
5208
3336 可以發現把關系看成邊的話這是一顆樹 設$f[i][j]$表示在以$i$為根的子數中,根節點是第$j$大 那麽考慮每次和一顆子樹的答案合並(顯然合並子樹的順序不影響答案) 對於每一個$f[u][j]$來說,當它在合並時孩子$v$時 先不考慮$u$和$v$兩顆樹本身的以及$u$和$v$之間的大小關系,那麽可以 枚舉子樹中前$k$小的點比根小,那麽這一步就有$C_{j+k-1}^{k}*C_{siz[u]+siz[v]-j-k}^{siz[v]-k}$種方案 這是因為把一個子樹從小到大攤成一個序列後 $u$前面有$j+k-1$個位置放點,其中選$k$個給$v$,$u$後面有&siz[u]+siz[v]-j-k&個位置放點,其中選$siz[v]-k$個給孩子
#pragma GCC optimize("O2") #include <cstdio> #include <cstring> char buf[10000000], *ptr = buf - 1; inline int readint(){ int n = 0; char ch = *++ptr; while(ch < ‘0‘ || ch > ‘9‘) ch = *++ptr; while(ch <= ‘9‘ && ch >= ‘0‘){ n = (n << 1) + (n << 3) + (ch ^ 48); ch = *++ptr; } return n; } typedef long long ll; const int maxn = 1000 + 10, mod = 1000000007; inline void add(ll &x, ll y){ x = (x + y) % mod; } ll C[maxn][maxn]; struct Edge{ int to, val, next; // (val = 1) <=> ‘>‘ ; (val = 0) <=> ‘<‘ Edge(){} Edge(int _t, int _v, int _n): to(_t), val(_v), next(_n){} }e[maxn * 2]; int fir[maxn], cnt; inline void ins(int u, int v, int w){ e[++cnt] = Edge(v, w, fir[u]); fir[u] = cnt; e[++cnt] = Edge(u, w ^ 1, fir[v]); fir[v] = cnt; } int n; ll sum[maxn][maxn]; ll f[maxn][maxn], tp[maxn]; int siz[maxn]; void dfs(int u, int fa){ siz[u] = f[u][1] = 1; for(int v, i = fir[u]; i; i = e[i].next){ v = e[i].to; if(v == fa) continue; dfs(v, u); for(int j = siz[u] + siz[v]; j; j--) tp[j] = 0; // u > v if(e[i].val == 1) for(int j = 1; j <= siz[u]; j++) for(int k = 1; k <= siz[v]; k++) add(tp[j + k], C[j + k - 1][k] * C[siz[u] + siz[v] - j - k][siz[v] - k] % mod * f[u][j] % mod * sum[v][k]); else for(int j = 1; j <= siz[u]; j++) for(int k = 0; k < siz[v]; k++) add(tp[j + k], C[j + k - 1][k] * C[siz[u] + siz[v] - j - k][siz[v] - k] % mod * f[u][j] % mod * (sum[v][siz[v]] - sum[v][k] + mod)); siz[u] += siz[v]; for(int j = 1; j <= siz[u]; j++) f[u][j] = tp[j]; } sum[u][0] = 0; for(int i = 1; i <= siz[u]; i++){ sum[u][i] = sum[u][i - 1] + f[u][i]; if(sum[u][i] >= mod) sum[u][i] -= mod; } } void init(){ n = readint(); cnt = 0; for(int i = 0; i < n; i++) fir[i] = 0; int u, v; char ch; for(int i = 1; i < n; i++){ u = readint(); ch = *++ptr; while(ch != ‘<‘ && ch != ‘>‘) ch = *++ptr; v = readint(); if(ch == ‘>‘) ins(u, v, 1); else ins(u, v, 0); } for(int i = 0; i < n; i++) for(int j = 1; j <= n; j++) f[i][j] = 0; } void init_C(){ for(int i = 0; i <= 1000; i++) C[i][0] = 1; for(int i = 1; i <= 1000; i++) for(int j = 1; j <= i; j++){ C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; if(C[i][j] >= mod) C[i][j] -= mod; } } int main(){ fread(buf, sizeof(char), sizeof(buf), stdin); init_C(); int T = readint(); while(T--){ init(); dfs(0, -1); printf("%lld\n", sum[0][n]); } return 0; }
[BZOJ3167][Heoi2013]Sao