T1;

  简化问题,比较容易得到排序策略,然而这种排序策略仅局限于子节点决策

考虑直观想法显然为树形DP,然而是错的,考虑问题在于本题的选择策略并不是连续的

也就是并不一定选择完一个子树再选择下一个

  这也启示我们树形DP的局限性,即问题必须具有连续性与可合并性

  考虑修正,既然选择不一定是连续的,那么我们就每次选择最优选项进行合并

即通过同样的排序策略,每次全局寻找最优点与父亲进行合并代表选择父亲后要选择它

每次更新父亲的排序关键字,利用优先队列或set维护即可

  考虑一种贪心的假做法,即每次从根开始从子节点中选择最优点并更新子节点序列

问题仍然在于贪心的短视性,而正解通过合并再更新父节点的关键字则可以解决这一问题

代码如下:

NOIP模拟67_子节点NOIP模拟67_子节点_02
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define B bool
 6 #define V void
 7 #define D double
 8 #define LL long long
 9 #define UI unsigned int
10 #define UL unsigned long long
11 #define P pair<I,I>
12 #define MP make_pair
13 #define a first
14 #define b second
15 #define lowbit(x) (x & -x)
16 #define debug cout << "It's Ok Here !" << endl;
17 #define FP(x) freopen (#x,"r",stdin)
18 #define FC(x) freopen (#x,"w",stdout)
19 const I N = 3e5 + 3;
20 I n,f[N],sigma,ans;
21 struct T {I idx,a,b;} a[N];
22 multiset <T> s;
23 inline I read () {
24     I x(0),y(1); C z(getchar());
25     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
26     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
27     return x * y;
28 }
29 inline V Max (I &a,I b) { a = a > b ? a : b; }
30 inline V Min (I &a,I b) { a = a < b ? a : b; }
31 inline I max (I a,I b) { return a > b ? a : b; }
32 inline I min (I a,I b) { return a < b ? a : b; }
33 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; }
34 inline I abs (I a) { return a >= 0 ? a : -a; }
35 inline P operator + (const P &a,const P &b) {
36     return MP (a.a + b.a,a.b + b.b);
37 }
38 inline P operator - (const P &a,const P &b) {
39     return MP (a.a - b.a,a.b - b.b);
40 }
41 inline B operator < (const T &a,const T &b) {
42     return a.b * b.a == b.b * a.a ? a.idx < b.idx : a.b * b.a < b.b * a.a;
43 }
44 struct DJS {
45     I f[N];
46     inline V initital () {
47         for (I i(1);i <= n; ++ i) f[i] = i;
48     }
49     I get (I x) {
50         return x == f[x] ? x : f[x] = get (f[x]);
51     }
52     inline V merge (I x,I y) {
53         if (x == y) return ;
54         I fx (get (x)), fy (get (y));
55         if (fx == fy) return ; 
56         f[fy] = fx; if (fx != 1) s.erase (s.find (a[fx]));
57         a[fx].a += a[fy].a, a[fx].b += a[fy].b; if (fx != 1) s.insert (a[fx]);
58     }
59 }DJS;
60 signed main () {
61     n = read (); DJS.initital ();
62     for (I i(2);i <= n; ++ i) 
63         f[i] = read ();
64     for (I i(1);i <= n; ++ i) 
65         a[i].idx = i, a[i].a = read (), a[i].b = read ();
66     for (I i(2);i <= n; ++ i)
67         sigma += a[i].a, s.insert (a[i]);
68     ans += a[1].b * sigma;
69     for (I i(1);i <  n; ++ i) {
70         auto it = -- s.end (); s.erase (it); T x (*it); I tmp (DJS.get (f[x.idx]));
71         ans += tmp == 1 ? a[x.idx].b * (sigma - a[x.idx].a), sigma -= a[x.idx].a : a[tmp].b * a[x.idx].a;
72         DJS.merge (f[x.idx],x.idx);  
73     }
74     printf ("%lld\n",ans);
75 }
View Code

T2:

  考虑突破点在于如何判断合法性,显然若子树总a值 >= 子树size则可行,那么考虑可行下的方案数

显然为子树总a值 - 子树总size即为rest,考虑树形DP子树合并,首先直接继承子节点方案数,考虑子节点

间合并所产生的方案数,考虑首先选择子节点上部节点一定在子节点下部节点之前,那么分开考虑

于是方案数显然为子树合并过程中顺序不同所产生的方案数,即对于相对顺序相同的序列x,y,其合并方案数

为C(x + y,y),于是在合并过程中记录最终累加即可

代码如下:

NOIP模拟67_子节点NOIP模拟67_子节点_02
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define B bool
 6 #define V void
 7 #define D double
 8 #define LL long long
 9 #define UI unsigned int
10 #define UL unsigned long long
11 #define P pair<I,I>
12 #define MP make_pair
13 #define a first
14 #define b second
15 #define lowbit(x) (x & -x)
16 #define debug cout << "It's Ok Here !" << endl;
17 #define FP(x) freopen (#x,"r",stdin)
18 #define FC(x) freopen (#x,"w",stdout)
19 const I N = 1e6 + 3, mod = 1e9 + 7;
20 I n,f[N],a[N],J[N],Y[N],size[N],dp[N];
21 I tot,to[N << 1],nxt[N << 1],head[N];
22 inline I read () {
23     I x(0),y(1); C z(getchar());
24     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
25     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
26     return x * y;
27 }
28 inline V Max (D &a,D b) { a = a > b ? a : b; }
29 inline V Min (D &a,D b) { a = a < b ? a : b; }
30 inline D max (D a,D b) { return a > b ? a : b; }
31 inline D min (D a,D b) { return a < b ? a : b; }
32 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; }
33 inline I abs (I a) { return a >= 0 ? a : -a; }
34 inline P operator + (const P &a,const P &b) {
35     return MP (a.a + b.a,a.b + b.b);
36 }
37 inline P operator - (const P &a,const P &b) {
38     return MP (a.a - b.a,a.b - b.b);
39 }
40 inline V found (I x,I y) {
41     to[++tot] = y, nxt[tot] = head[x], head[x] = tot;
42     to[++tot] = x, nxt[tot] = head[y], head[y] = tot;
43 }
44 inline I fp (I a,I b) { I ans (1);
45     for (; b ;b >>= 1,a = a * a % mod)
46         if(b & 1) ans = ans * a % mod;
47     return ans;
48 }
49 inline I _C (I n,I m) {
50     return J[n] * Y[m] % mod * Y[n - m] % mod;
51 }
52 V Dfs (I x) {
53     I tmp1 (1), tmp2 (1); size[x] = dp[x] = 1; 
54     for (I i(head[x]),y(to[i]); i ;i = nxt[i],y = to[i]) if (y != f[x]) {
55         Dfs (y), size[x] += size[y]; a[x] += a[y]; (dp[x] *= dp[y]) %= mod; 
56         (tmp1 *= _C (a[x] - size[x] + 1,a[y] - size[y])) %= mod;  
57         (tmp2 *= _C (size[x] - 1,size[y])) %= mod;
58     }                      
59     (dp[x] *= tmp1 * tmp2 % mod) %= mod;
60 }
61 signed main () {
62     FP (ball.in), FC (ball.out);
63     n = read ();
64     for (I i(2);i <= n; ++ i)
65         found (f[i] = read (),i);
66     for (I i(1);i <= n; ++ i) 
67         a[i] = read ();
68     J[0] = Y[0] = 1;
69     for (I i(1);i <= n; ++ i)
70         J[i] = J[i - 1] * i % mod;
71     Y[n] = fp (J[n],mod - 2);
72     for (I i(n - 1); i; -- i)
73         Y[i] = Y[i + 1] * (i + 1) % mod;
74     Dfs (1);
75     for (I i(1);i <= n; ++ i)
76         cout << i << " : " << dp[i] << endl;
77     printf ("%lld\n",dp[1]);
78 }
View Code