NOIP模拟67
T1;
简化问题,比较容易得到排序策略,然而这种排序策略仅局限于子节点决策
考虑直观想法显然为树形DP,然而是错的,考虑问题在于本题的选择策略并不是连续的
也就是并不一定选择完一个子树再选择下一个
这也启示我们树形DP的局限性,即问题必须具有连续性与可合并性
考虑修正,既然选择不一定是连续的,那么我们就每次选择最优选项进行合并
即通过同样的排序策略,每次全局寻找最优点与父亲进行合并代表选择父亲后要选择它
每次更新父亲的排序关键字,利用优先队列或set维护即可
考虑一种贪心的假做法,即每次从根开始从子节点中选择最优点并更新子节点序列
问题仍然在于贪心的短视性,而正解通过合并再更新父节点的关键字则可以解决这一问题
代码如下:
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 }
T2:
考虑突破点在于如何判断合法性,显然若子树总a值 >= 子树size则可行,那么考虑可行下的方案数
显然为子树总a值 - 子树总size即为rest,考虑树形DP子树合并,首先直接继承子节点方案数,考虑子节点
间合并所产生的方案数,考虑首先选择子节点上部节点一定在子节点下部节点之前,那么分开考虑
于是方案数显然为子树合并过程中顺序不同所产生的方案数,即对于相对顺序相同的序列x,y,其合并方案数
为C(x + y,y),于是在合并过程中记录最终累加即可
代码如下:
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 }