[关闭]
@rebirth1120 2019-08-08T08:17:02.000000Z 字数 2289 阅读 659

AT3536 Bichrome Tree

dp 解题报告


题目链接

题目大意

现有一棵有 个节点的树,根节点为 1,每个节点可以染成黑色或白色。
给出一个长度为 的序列,分别为 ,你可以将每个节点赋一个非负整数值,问是否能使以节点 为根的子树中,与 颜色相同的节点(包括 本身)的权值和等于 中的每一个数都要满足)。

解题思路

一眼dp
看到这题后马上反应树型dp,然后……就没有然后了……

仔细想一下,其实这题在转移的时候并不用考虑把当前点染成哪种颜色,因为它和它的子节点的关系只有两种:颜色相同,颜色不同。 而两个子树的颜色是不会互相干扰的,所以,我们在dp时只需决定每个子节点的颜色是否与根节点相同,而无需考虑它要选那种颜色(相当于可以使整个子树的颜色翻转)。

那么,我们怎么样才能使节点的赋值情况尽量满足题目的要求(即如何做到最有决策)呢?
先考虑一下什么情况下是没办法满足要求的,
对于一个节点,它的每一个子节点都有两个值(白色和黑色),而对于每一个子节点,它都只能且必须选一个值,由于我们为节点赋的值必须是非负整数,所以如果节点 所有的子节点的两个值中的较小值的和 那么就无法满足题目的要求。
为了避免这种情况出现,我们就要使每个节点的值尽量地小。
为节点 的值, 为节点 的子树中(不包括它本身)与它颜色相同的点的权值和,可得出式子 ,我们要使 尽量地小,那么就要使 尽量地接近 ,即 为小于等于 的最大值。
那么,我们的问题就转化为:
个数,每个数有两个值 ,每个数必须且只能取一个值,求一个最优方案,使 个数的权值之和小于等于定值 ,且最大。

枚举?……

貌似背包可以解决,但是我不会……

那看看还能不能进一步简化问题。

先想想对每个节点要进行的操作,

对于每一个节点 ,我们先要取它的每个子树的两个权值的较小值之和,看它是否小于等于 ,若满足的话,那我们就可以试着用每一个子树的较大值替换它的较小值,使 尽量接近 ,设子树 的较大值和较小值分别为 ,那么,若用子树 的较大值替换它的较小值,对答案的贡献为
我们设 ,并将每棵子树 存起来(设为 ,那么我们的问题就转化为了:
个数,在这 个数中取任意个数,使它们的和尽量接近
现在dp貌似就可做了,
为考虑到第 位(且取第 个数)取了 个数的和的最大值(小于等于 ),转移方程为:


取个max就行了,
结果还是 ……
加个堆优化,
看眼数据范围

能过。

那么这道题的思路基本上就到位了,实现起来还是挺简单的,简单来说就是 dp 套 dp……

代码实现

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<queue>
  6. using namespace std;
  7. const int N=1000+7;
  8. int n,size[N],w[N];
  9. int lst[N],nxt[N],to[N],tot;
  10. void add(int x,int y){
  11. nxt[++tot]=lst[x];
  12. to[tot]=y;
  13. lst[x]=tot;
  14. }
  15. void init(){
  16. cin>>n;
  17. int fa;
  18. for(int i=2;i<=n;i++){
  19. scanf("%d",&fa);
  20. add(fa,i);
  21. }
  22. for(int i=1;i<=n;i++) scanf("%d",&w[i]);
  23. }
  24. bool run(int u){
  25. int sum=0,cnt=0,box[N]; // box数组一定要定义在函数内,不然会受到下一层递归的影响
  26. for(int i=lst[u];i;i=nxt[i]){
  27. int v=to[i];
  28. if(!run(v)) return 0;
  29. size[u]+=size[v];
  30. int les=min(w[v],size[v]-w[v]);
  31. int lar=max(w[v],size[v]-w[v]);
  32. sum+=les;
  33. box[++cnt]=lar-les;
  34. }
  35. if(sum>w[u]){ return 0; }
  36. sum=w[u]-sum; int maxn=0;
  37. sort(box+1,box+1+cnt);
  38. priority_queue<int> h[cnt+7];
  39. for(int i=1;i<=cnt;i++){
  40. if(box[i]>sum) break;
  41. for(int j=i;j>=1;j--){
  42. int tmp=0;
  43. if(!h[j-1].empty()) tmp=h[j-1].top();
  44. while(!h[j-1].empty()&&tmp+box[i]>sum){ h[j-1].pop();tmp=h[j-1].top(); }
  45. if(tmp+box[i]>sum) continue;
  46. h[j].push(tmp+box[i]);
  47. maxn=max(maxn,tmp+box[i]);
  48. }
  49. }
  50. size[u]+=sum-maxn;
  51. return 1;
  52. }
  53. int main(){
  54. //freopen("tree.in","r",stdin);
  55. init();
  56. if(run(1)) printf("POSSIBLE\n");
  57. else printf("IMPOSSIBLE\n");
  58. return 0;
  59. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注