正睿20NOIp前冲刺day8
估分:100+20+0+0=120
实际:100+20+0+0=120
T1:
树形DP,f[i]表示以i为根的子树内所有点全遍历的最小花费,rev[i]表示遍历完子树所有点后回到i点需要的花废。dep[i]表示i的深度。设u的子节点有i,j,先遍历i,则有f[u]+=f[i],若rev[i]+1<dep[u],那遍历j的时候直接从i回到u再遍历j一定比传送回1节点再遍历到j优,则f[u]+=rev[i]+1+f[j]-dep[u]。所以f[u]先加上所有子节点的f,若有子节点i的rev+1<dep[u],那f[u]+=rev[i]+1-dep[u],但这样若所有字节点的rev+1都小于dep[u]的话会把所有的dep[u]全减掉,就会变成下图的样子:
所以当这种情况时,记录一下rev最大的子节点的rev,最后再减掉,加上dep就行了。
之后再看每个节点的rev应该是多少,可以发现,若当前节点i并不是所有子节点的rev+1都小于dep[i],那他一定不会往上走到父节点,而是直接传送回1号节点重新走到i的父节点u。因为若i不是所有子节点的rev+1都小于dep[i],那任意一个rev+1>=dep[i],若用这个rev回到u的话,他的花费一定是rev+2,一定是大于等于dep[u]的,所以肯定不用。所以只有i的所有子节点的rev+1全部都<dep[i]时,从i往回走到u才更优。这样DP就出来了
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 1000010; 8 const int INF = 0x3f3f3f3f; 9 10 int n; 11 int dep[N], f[N], rev[N]; 12 int h[N], num[N << 1], nex[N << 1], dqx; 13 14 void add(int a, int b) 15 { 16 num[dqx] = b; 17 nex[dqx] = h[a]; 18 h[a] = dqx++; 19 } 20 21 void dfs(int u, int pa) 22 { 23 int cnt = 0, res = 0; 24 for (int i = h[u]; ~i; i = nex[i]) 25 { 26 int j = num[i]; 27 if (j == pa) continue; 28 dep[j] = dep[u] + 1; 29 dfs(j, u); 30 f[u] = f[u] + f[j]; 31 if (rev[j] + 1 <= dep[u]) 32 { 33 f[u] += rev[j] + 1 - dep[u]; 34 rev[u] = max(rev[u], rev[j] + 1); 35 cnt++; 36 } 37 res++; 38 } 39 if (cnt != res) rev[u] = INF; 40 else f[u] += dep[u] - rev[u]; 41 if (nex[h[u]] == -1 && u != 1) rev[u] = 0, f[u] = dep[u]; 42 } 43 44 int main() 45 { 46 scanf("%d", &n); 47 memset(h, -1, sizeof(h)); 48 for (int i = 2; i <= n; i++) 49 { 50 int a; 51 scanf("%d", &a); 52 add(a, i), add(i, a); 53 } 54 dfs(1, 0); 55 printf("%d", f[1]); 56 }
T2:
20pts暴力
直接放题解,我说不清楚
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 5010; 8 const int mod = 1e9 + 7; 9 10 int n, m; 11 int fac[N * 2], inv[N * 2], f[N][N], s[N]; 12 13 int C(int n, int m) 14 { 15 return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; 16 } 17 18 int qpow(int a, int k) 19 { 20 int res = 1; 21 while (k) 22 { 23 if (k & 1) res = 1ll * res * a % mod; 24 a = 1ll * a * a % mod; 25 k >>= 1; 26 } 27 return res; 28 } 29 30 int main() 31 { 32 scanf("%d%d", &n, &m); 33 34 fac[0] = inv[0] = 1; 35 for (int i = 1; i <= n + m; ++i) 36 { 37 fac[i] = 1ll * fac[i - 1] * i % mod; 38 inv[i] = qpow(fac[i], mod - 2); 39 } 40 for (int i = 1; i <= n; ++i) 41 { 42 for (int j = 1; i * j <= m; ++j) 43 { 44 for (int k = i, op = 1; k <= n && j * k <= m; ++k, op = mod - op) 45 { 46 f[i][j] = (f[i][j] + 1ll * C(k, i) * op % mod * C(n, k) % mod * C(m - j * k + n - 1, n - 1)) % mod; 47 } 48 f[i][j] = 1ll * f[i][j] * qpow(C(m + n - 1, n - 1), mod - 2) % mod; 49 } 50 } 51 52 for (int i = n; i >= 1; --i) 53 { 54 s[i] = 1; 55 for (int j = 1; j <= m; ++j) 56 { 57 if ((f[i][j] += f[i + 1][j]) >= mod ) f[i][j] -= mod; 58 if ((s[i] += f[i][j]) >= mod ) s[i] -= mod; 59 } 60 } 61 for (int i = 1; i <= n; ++i) 62 { 63 if ((s[i] += s[i - 1]) >= mod) s[i] -= mod; 64 printf("%d\n", s[i]); 65 } 66 }
剩下两题不会
总结:
没啥说的,菜是原罪