典型树形dp

这里,我们应该看到一些基本性质:

①:如果这个边不能改(不是没有必要改),我们就不改,因为就算改过去还要改回来,显然不是最优的

注意:“不能改”是指边的性质和要求的相同而不包括对边的颜色没有要求的情况!

②:如果我们每翻转一条边,就认为将这条边的两个端点度数+1,那么不难看到,最后翻转的所有边构成的路径总数就是度数为奇数点个数的1/2

(性质②的证明:一条路径只会对两端的点产生度数上的影响,而中间的点都是+2,还是偶数,所以无影响)

接下来,我们进行dp:

记状态f[i][0/1]代表以i为根节点的子树,0/1代表根节点与父亲的边是否翻转

那么我们可以看到,这个dp包含两部分内容,一部分要保证路径数最少,另一部分要保证在满足路径数最小的前提下路径总长度最小,所以我们需要同时更新这两个值,这样用pair就是一个比较好的选择

接下来我们考虑更新:

在更新时我们使用两个参量:p和q,作为更新dp的中间步骤,用p代表不以i为端点做链,q代表以i为端点做链,设i的某个子节点为to,于是有:

其中p初始化为(0,0),q初始化为(INF,INF)

解释一下:这里pair的add就是对应元素相加(手写!非内置!)

而min操作表示先按pair第一关键字比较,再按第二关键字比较

那么这一步就是一个合并的过程:

首先,i不作为链的端点:分两类来合并:如果子节点与i的边翻转了,那么就要累在以i为端点的链里(因为i与父亲的边不翻转,那么i就将是个端点),如果子节点与i的边没有翻转,那么仅针对这棵子树而言,i并没有作为路径的端点,所以更新没有以i为端点链的代价

如果i作为链的端点,同样分两类来合并:如果子节点与i的边翻转了,那么i显然可以成为链的端点,前提是在此之前i并不是链的端点,所以用之前i不是链的端点的代价来更新;反之,如果子节点与i的边没有翻转,那么就此而言i并不是端点,可要求i是链的一个端点,这样就必须用i原先就是链的端点的代价来更新

遍历根节点所有子节点后,更新dp:

如果i与父亲的边没有翻转。即状态dp[i][0]:

首先,i不作为链的端点肯定是一种可能性,直接比较

接着,如果i是链的一个端点,i和父节点的边还没有翻转,那么说明在这个状态下i是真正的奇度点,所以将状态q的first+1后更新

如果i与父亲的边翻转了,同样分两类更新:

首先,i本身作为了链的端点,而由于i本身就是奇度点,所以仅需将q.second+1来更新即可

还有,如果i本身在下面并没有作为链的端点,而i却与父节点的边发生了翻转,所以i就成为了新的奇度点,同时链长还增加了,所以p.first,p.second均增加即可

最后答案即为dp[1][0].first/2,dp[1][0].second

  1. #include <cstdio>
  2. #include <cmath>
  3. #include <cstring>
  4. #include <cstdlib>
  5. #include <iostream>
  6. #include <algorithm>
  7. #include <queue>
  8. #include <stack>
  9. #define INF 0x3f3f3f3f
  10. using namespace std;
  11. struct Edge
  12. {
  13. int next;
  14. int to;
  15. int val;
  16. }edge[200005];
  17. int head[100005];
  18. int cnt=1;
  19. pair <int,int> dp[100005][2];
  20. int n;
  21. void init()
  22. {
  23. memset(head,-1,sizeof(head));
  24. cnt=1;
  25. }
  26. void add(int l,int r,int w)
  27. {
  28. edge[cnt].next=head[l];
  29. edge[cnt].to=r;
  30. edge[cnt].val=w;
  31. head[l]=cnt++;
  32. }
  33. pair<int,int> addp(pair<int,int> a,pair<int,int> b)
  34. {
  35. return make_pair(a.first+b.first,a.second+b.second);
  36. }
  37. void dfs(int x,int fx,int typ)
  38. {
  39. pair <int,int> p,q;
  40. p=make_pair(0,0);
  41. q=make_pair(INF,INF);
  42. for(int i=head[x];i!=-1;i=edge[i].next)
  43. {
  44. int to=edge[i].to;
  45. if(to==fx)
  46. {
  47. continue;
  48. }
  49. dfs(to,x,edge[i].val);
  50. pair <int,int> temp1,temp2;
  51. temp1=min(addp(p,dp[to][0]),addp(q,dp[to][1]));
  52. temp2=min(addp(p,dp[to][1]),addp(q,dp[to][0]));
  53. p=temp1;
  54. q=temp2;
  55. }
  56. if(typ==2||typ==0)
  57. {
  58. dp[x][0]=min(p,make_pair(q.first+1,q.second));
  59. }else
  60. {
  61. dp[x][0]=make_pair(INF,INF);
  62. }
  63. if(typ==2||typ==1)
  64. {
  65. dp[x][1]=min(make_pair(p.first+1,p.second+1),make_pair(q.first,q.second+1));
  66. }else
  67. {
  68. dp[x][1]=make_pair(INF,INF);
  69. }
  70. }
  71. int main()
  72. {
  73. freopen("w.in","r",stdin);
  74. freopen("w.out","w",stdout);
  75. scanf("%d",&n);
  76. init();
  77. for(int i=1;i<n;i++)
  78. {
  79. int x,y,z,w;
  80. scanf("%d%d%d%d",&x,&y,&z,&w);
  81. if(w==2)
  82. {
  83. add(x,y,w);
  84. add(y,x,w);
  85. }else
  86. {
  87. add(x,y,w^z);
  88. add(y,x,w^z);
  89. }
  90. }
  91. dfs(1,1,0);
  92. printf("%d %d\n",dp[1][0].first/2,dp[1][0].second);
  93. return 0;
  94. }

雅礼 noip2018 模拟赛 day3 T3的更多相关文章

  1. 雅礼 noip2018 模拟赛day3 T2

    典型的状压思想 设0表示黑球,1表示白球,用一串01序列代表剩下的球的状态,记f[i]表示在i状态下取球的最大期望 那么可以利用记忆化搜索更新,每一层枚举可能拿走的球然后向下搜索,同时记忆化即可 在状 ...

  2. NOIP2018 模拟赛(二十二)雅礼NOI

    Preface 这次的题目都是NOI+的题,所以大家的分数都有点惨烈. 依靠T1大力骗分水到Rank2 所以想看正解的话看这里吧 A. 「雅礼NOI2018模拟赛(一) Day1」树 看一眼题目感觉十 ...

  3. [NOIP2018模拟赛10.16]手残报告

    [NOIP2018模拟赛10.16]手残报告 闲扯 炉石乱斗模式美滋滋啊,又颓到好晚... 上来T2先敲了树剖,看T1发现是个思博DP,然后没过大样例,写个暴力发现还是没过大样例!?才发现理解错题意了 ...

  4. EZ 2018 06 10 NOIP2018 模拟赛(十八)

    好久没写blog&&比赛题解了,最近补一下 这次还是很狗的,T3想了很久最后竟然连并查集都忘写了,然后T2map莫名爆炸. Rating爆减......链接不解释 好了我们开始看题. ...

  5. [雅礼NOIP2018集训 day4]

    感觉状态极差啊,今天居然爆零了 主要是以下原因: 1.又是T1看错题肝了两个小时,发现题意理解错误瞬间心态爆炸 2.T2交错了文件名 3.T3暴力子任务和正解(假的)混在一起,输出了两个答案 都想为自 ...

  6. [NOIP2018模拟赛10.18]自闭报告

    闲扯 这一天,菜鸡RyeCatcher又想起来了被毒瘤题支配的恐惧 今天比较好玩,还是ljy提醒才发现文件夹里有题面...不知道外面的人什么时候才发现 看完了题面,又回到了雅礼啥题也不会写的感觉 T1 ...

  7. [NOIP2018模拟赛10.23]发呆报告

    闲扯 考场看了眼题目感觉很难,一个小时敲完了所有暴力...嗯然后就在那里发呆什么事也没做 T3考场上把数据结构想了个遍都不会完成1操作,现在看这种思路其实之前也接触过... 比较玄学的一件事情就是T1 ...

  8. [NOIP2018模拟赛10.22]咕咕报告

    闲扯 这是篇咕咕了的博客 考场上码完暴力后不知道干什么,然后忽然发现这个T1好像有点像一道雅礼集训时讲过的CF题目 Rest In Shades ,当时那道题还想了挺久不过思路比较妙,于是我就也\(y ...

  9. [NOIP2018模拟赛10.20A]挂分报告

    闲扯 先看看了B组,T1 ZROI刚好讲过一个性质原根一般很小的,直接枚举;T2一眼二分然后似乎状压 T3没看 然后上来A组题,T1 flow这名字...网络流?! T1题面非常的社会主义核心价值观, ...

随机推荐

  1. 关于vmvawe的光驱,iso镜像,挂载,卸载

    勾选这个使用iso镜像文件,就相当于真实的环境下,将一张光盘插进了光驱里,然后再勾选启动时连接,那么在linux开机后,/dev/cdrom或者/dev/sr0(前者是后者的软连接)就表示这个硬件设备 ...

  2. python小练习---TCP服务器端

    针对于上一篇分享python小练习---TCP客户端 http://www.cnblogs.com/zhaijiahui/p/6926197.html我继续按书中内容,向下进行这里需要强调一个事py3 ...

  3. 同步&异步+阻塞&非阻塞(理解)

    0 - 同步&异步 同步和异步关注的是消息通信机制. 0.1 - 同步 由“调用者”主动等待这个“调用”结果.即是,发出一个“调用”时,在没有得到结果之前,该“调用”不返回,一旦调用返回,则得 ...

  4. java--GC Root有哪些

    GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收.方法区.JVM栈和Native栈不被GC所管理,因而选择这些非堆区的对象作为GC roots,被GC roots引用的对象不被GC回收. ...

  5. setInterval 传值设参数

    <script type="text/javascript" > window.onload=function(){ for(var i=1;i<3;i++){ ...

  6. 51nod 1379 索函数

    Fib[0]=0,Fib[1]=1,Fib[n]=Fib[n-1]+Fib[n-2] if n>1. 定义索函数Sor(n)=Fib[0]| Fib[1] |Fib[2]|…|Fib[n]. 给 ...

  7. 记录几个GIT命令

    更新:git pull 删除未提交的文件:git clean -f 删除未提交的文件夹:git clean -fd 查看状态:git status 查看分支:git branch 检出某分支:git ...

  8. MySql DDL语言(数据库和数据表的管理)

    数据定义语言,负责数据库和数据表的管理 ⒈数据库的管理 1.创建数据库 create database if not exists DatabaseName; #if not exists可以省略 2 ...

  9. 二层环路保护,SEP多实例的配置

    作者:邓聪聪 智能以太保护SEP(Smart Ethernet Protection)是一种专用于以太网链路层的环网协议.SEP是一种以太环路保护机制,它通过有选择性地阻塞网络环路冗余链路,来达到消除 ...

  10. JavaScript拼接html字符串时截断问题

    在项目中碰到一个问题,就是JavaScript拼接html标签时,里面特殊字符会有些问题,比如单引号截断配对,导致后面的内容不显示或显示错误.在此记录一下. 下面贴一段简化的代码,若有描述不清的地方还 ...