题面

题解

先解决第一个子问题吧,它才是难点

Subtask_1

我们可以先用一个简单的树形DP处理出每棵树内部的dis和,记为dp0[i],

然后再用一个换根的树形DP处理出每棵树内点 i 到树内每个点的距离和,记为dp[i],

好,现在分两个连通块跟三个连通块两种情况讨论

两个连通块

把两棵树A,B合并到一起,我们得先确定两个连接的点,

若其分别为 i,j,不难发现答案就是 dp0[A] + dp0[B] + dp[i] * size[B] + dp[j] * size[A] + size[A] * size[B]

其中,dp0[A],dp0[B],size[A],size[B]都是确定的,那么当答案取最大的时候,dp[i]、dp[j]一定分别都取最大

所以在两棵树中找dp值最大的两个点 i , j 就行了。

三个连通块

两条边连接三棵树,幸运的是,大的情况只有三种(A-B-C , A-C-B , B-A-C)

其中一棵树一定连了两条边,且不一定是同一个点连出去,不妨设中间那棵为B

注意到中间那条绿色的路径没?也就是说中间是可能有两个点的

若连接的两个点分别为 i,j,那么仔细开动一下脑筋,会发现总的贡献是

其中随着 i,j 变化的只有  

因此,若以B树上每个节点为 i 考虑,我们可以用一个简单的换根DP求出上式的最大值,记为dp2[i],然后再求出B树中最大的dp2[i],加到上面三排的式子中,答案就出来了。

Subtask_2

子问题二其实更简单,不用管它的第二个条件,因为它只有0~1个解。

原图是个森林,是很多树组成,所以先考虑叶子结点,叶子结点如果是黑的,它的父边就不能删,如果是白的,它的父边就必须删;然后消除它父边的影响,再把叶子删去。这样一来,又有新的叶子,重复考虑……可以发现,最后要么无解,要么只有一个解,而且很好输出。

CODE

  1. #include<map>
  2. #include<queue>
  3. #include<cmath>
  4. #include<vector>
  5. #include<cstdio>
  6. #include<cstring>
  7. #include<iostream>
  8. #include<algorithm>
  9. using namespace std;
  10. #define MAXN 100005
  11. #define LL unsigned long long
  12. #define DB double
  13. #define ENDL putchar('\n')
  14. #define lowbit(x) ((-x)&(x))
  15. LL read() {
  16. LL f = 1,x = 0;char s = getchar();
  17. while(s < '0' || s > '9') {if(s=='-')f=-f;s = getchar();}
  18. while(s >= '0' && s <= '9') {x=x*10+(s-'0');s=getchar();}
  19. return f*x;
  20. }
  21. const int MOD = 998244353;
  22. int n,m,i,j,s,o,k;
  23. vector<int> g[MAXN];
  24. vector<int> id[MAXN];
  25. char cl[MAXN];
  26. int U[MAXN],V[MAXN];
  27. int siz[MAXN],rt[10],cnt;
  28. LL dp1[MAXN],dp2[MAXN],ma[MAXN],sm[MAXN],dp[MAXN];
  29. LL dpu[MAXN],dpd[MAXN],dpa[MAXN];
  30. vector<LL> pre[MAXN],suf[MAXN];
  31. bool vis[MAXN],f[MAXN],ad[MAXN];
  32. void dfs1(int x,int fa) {
  33. vis[x] = 1;
  34. siz[x] = 1;
  35. dp1[x] = 0;
  36. for(int i = 0;i < g[x].size();i ++) {
  37. int y = g[x][i];
  38. if(y != fa) {
  39. dfs1(y,x);
  40. siz[x] += siz[y];
  41. dp1[x] += dp1[y] + (LL)siz[y];
  42. }
  43. }return ;
  44. }
  45. void dfs2(int x,int fa,int n) {
  46. dp2[x] = 0;
  47. if(fa) {
  48. dp2[x] = (dp2[fa] + (dp1[fa] - (dp1[x] + (LL)siz[x]))) + (n-siz[x]);
  49. }
  50. dp[x] = dp1[x] + dp2[x];
  51. ma[x] = dp[x];
  52. sm[x] = siz[x] * (n-siz[x]);
  53. for(int i = 0;i < g[x].size();i ++) {
  54. int y = g[x][i];
  55. if(y != fa) {
  56. dfs2(y,x,n);
  57. ma[x] = max(ma[x],ma[y]);
  58. sm[x] += sm[y];
  59. }
  60. }
  61. return ;
  62. }
  63. void dfs3(int x,int fa,int sizA,int sizB) {
  64. dpd[x] = dp[x] * (LL)sizB + sizA *2ll* sizB;
  65. LL pr = 0;
  66. for(int i = 0;i < g[x].size();i ++) {
  67. pre[x].push_back(pr);
  68. int y = g[x][i];
  69. if(y != fa) {
  70. dfs3(y,x,sizA,sizB);
  71. pr = max(pr,dpd[y] + sizA *1ll* sizB);
  72. dpd[x] = max(dpd[x],dpd[y] + sizA *1ll* sizB);
  73. }
  74. }
  75. pr = 0;
  76. for(int i = (int)g[x].size()-1;i >= 0;i --) {
  77. suf[x].push_back(pr);
  78. int y = g[x][i];
  79. if(y != fa) {
  80. pr = max(pr,dpd[y] + sizA *1ll* sizB);
  81. }
  82. }
  83. return ;
  84. }
  85. void dfs4(int x,int fa,int sizA,int sizB,int ad1,int ad2) {
  86. dpu[x] = dp[x] * (LL)sizB + sizA *2ll* sizB;
  87. if(fa) {
  88. dpu[x] = max(dpu[x],max(dpu[fa],max(pre[fa][ad1],suf[fa][ad2])) + sizA *1ll* sizB);
  89. }
  90. dpa[x] = max(dpd[x],dpu[x]) + dp[x] * (LL)sizA;
  91. for(int i = 0;i < g[x].size();i ++) {
  92. int y = g[x][i];
  93. if(y != fa) {
  94. dfs4(y,x,sizA,sizB,i,(int)g[x].size()-1-i);
  95. dpa[x] = max(dpa[x],dpa[y]);
  96. }
  97. }return ;
  98. }
  99. bool dfs5(int x,int fa,int ed) {
  100. for(int i = 0;i < g[x].size();i ++) {
  101. int y = g[x][i],idn = id[x][i];
  102. if(y != fa) {
  103. bool cg = dfs5(y,x,idn);
  104. f[x] ^= cg;
  105. }
  106. }
  107. if(f[x]) ad[ed] = 1;
  108. return f[x];
  109. }
  110. int main() {
  111. // freopen("lct.in","r",stdin);
  112. // freopen("lct.out","w",stdout);
  113. n = read();m = read();
  114. scanf("%s",cl + 1);
  115. for(int i = 1;i <= m;i ++) {
  116. s = read();o = read();
  117. U[i] = s;V[i] = o;
  118. g[s].push_back(o);
  119. g[o].push_back(s);
  120. id[s].push_back(i);
  121. id[o].push_back(i);
  122. }
  123. for(int i = 1;i <= n;i ++) {
  124. if(!vis[i]) {
  125. dfs1(i,0);
  126. dfs2(i,0,siz[i]);
  127. rt[++ cnt] = i;
  128. }
  129. }
  130. LL ans1 = 0;
  131. if(cnt == 2) {
  132. ans1 = ma[rt[1]] * siz[rt[2]] + ma[rt[2]] * siz[rt[1]] + siz[rt[2]] *1ll* siz[rt[1]];
  133. ans1 += sm[rt[1]] + sm[rt[2]];
  134. }
  135. else if(cnt == 3) {
  136. LL DP1 = ma[rt[1]] * (LL)(n-siz[rt[1]]),D1 = siz[rt[1]] *1ll* (n-siz[rt[1]]);
  137. LL DP2 = ma[rt[2]] * (LL)(n-siz[rt[2]]),D2 = siz[rt[2]] *1ll* (n-siz[rt[2]]);
  138. LL DP3 = ma[rt[3]] * (LL)(n-siz[rt[3]]),D3 = siz[rt[3]] *1ll* (n-siz[rt[3]]);
  139. dfs3(rt[1],0,siz[rt[2]],siz[rt[3]]);
  140. dfs4(rt[1],0,siz[rt[2]],siz[rt[3]],0,0);
  141. ans1 = max(ans1,dpa[rt[1]] + DP2 + DP3 + D1);
  142. dfs3(rt[2],0,siz[rt[1]],siz[rt[3]]);
  143. dfs4(rt[2],0,siz[rt[1]],siz[rt[3]],0,0);
  144. ans1 = max(ans1,dpa[rt[2]] + DP1 + DP3 + D2);
  145. dfs3(rt[3],0,siz[rt[1]],siz[rt[2]]);
  146. dfs4(rt[3],0,siz[rt[1]],siz[rt[2]],0,0);
  147. ans1 = max(ans1,dpa[rt[3]] + DP1 + DP2 + D3);
  148. ans1 += sm[rt[1]] + sm[rt[2]] + sm[rt[3]];
  149. }
  150. printf("%lld\n",ans1);
  151. for(int i = 1;i <= n;i ++) f[i] = (cl[i] == 'B' ? 1:0);
  152. bool flag = 0;
  153. for(int i = 1;i <= cnt;i ++) {
  154. flag |= dfs5(rt[i],0,0);
  155. }
  156. if(flag) {
  157. printf("-1\n");
  158. }
  159. else {
  160. int cn = 0;
  161. for(int i = 1;i <= m;i ++) {
  162. if(ad[i]) cn ++;
  163. }
  164. printf("%d\n",cn);
  165. for(int i = 1;i <= m;i ++) {
  166. if(ad[i]) printf("%d ",i);
  167. }
  168. ENDL;
  169. }
  170. return 0;
  171. }

模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)的更多相关文章

  1. [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]

    题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...

  2. 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市

    P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...

  3. 树形dp换根,求切断任意边形成的两个子树的直径——hdu6686

    换根dp就是先任取一点为根,预处理出一些信息,然后在第二次dfs过程中进行状态的转移处理 本题难点在于任意割断一条边,求出剩下两棵子树的直径: 设割断的边为(u,v),设down[v]为以v为根的子树 ...

  4. bzoj 3743 [Coci2015]Kamp——树形dp+换根

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743 树形dp+换根. “从根出发又回到根” 减去 “mx ” . 注意dfsx里真的要改那 ...

  5. 【碳硫磷模拟赛】消失的+和* (树形DP)

    好久没做过这么恶心的DP题了 题面 题面很简单,有一个计算式,由+号.*号.括号和小于10的正整数组成,现在所有的+和*(由于属于违禁词而)都被-号给和谐掉了,现在要求所有可能的原计算式的结果之和. ...

  6. 7.18 NOI模拟赛 树论 线段树 树链剖分 树的直径的中心 SG函数 换根

    LINK:树论 不愧是我认识的出题人 出的题就是牛掰 == 他好像不认识我 考试的时候 只会写42 还有两个subtask写挂了 拿了37 确实两个subtask合起来只有5分的好成绩 父亲能转移到自 ...

  7. [题解](树形dp/换根)小x游世界树

    2. 小x游世界树 (yggdrasi.pas/c/cpp) [问题描述] 小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当 ...

  8. poj3585 Accumulation Degree(树形dp,换根)

    题意: 给你一棵n个顶点的树,有n-1条边,每一条边有一个容量z,表示x点到y点最多能通过z容量的水. 你可以任意选择一个点,然后从这个点倒水,然后水会经过一些边流到叶节点从而流出.问你最多你能倒多少 ...

  9. 树链剖分(附带LCA和换根)——基于dfs序的树上优化

    .... 有点懒: 需要先理解几个概念: 1. LCA 2. 线段树(熟练,要不代码能调一天) 3. 图论的基本知识(dfs序的性质) 这大概就好了: 定义: 1.重儿子:一个点所连点树size最大的 ...

随机推荐

  1. 浅谈倍增法求解LCA

    Luogu P3379 最近公共祖先 原题展现 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入格式 第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数.询问 ...

  2. 【Java面试】为什么引入偏向锁、轻量级锁,介绍下升级流程

    Hi,我是Mic 一个工作了7年的粉丝来找我,他说最近被各种锁搞晕了. 比如,共享锁.排它锁.偏向锁.轻量级锁.自旋锁.重量级锁. 间隙锁.临键锁.意向锁.读写锁.乐观锁.悲观锁.表锁.行锁. 然后前 ...

  3. Museui 图标速览,再也不用担心网页打不开了

    更多内容请见原文,原文转载自:https://blog.csdn.net/weixin_44519496/article/details/119328173

  4. SAP Drag or drop tree

    1 *&---------------------------------------------------------------------* 2 *& Report RSDEM ...

  5. mysql-安装(windows版本)与登录

    安装mysql 1.MySQL版本 mysql-5.6.35-winx64.zip 2.首先解压到安装目录 3.修改配置文件 复制my-default.ini 重命名为my.ini 然后修改mysql ...

  6. NC20806 区区区间间间

    NC20806 区区区间间间 题目 题目描述 给出长度为n的序列a,其中第i个元素为 \(a_i\),定义区间(l,r)的价值为 \(v_{l,r} = max(a_i - a_j | l \leqs ...

  7. 聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)

    本系列Netty源码解析文章基于 4.1.56.Final版本 在上篇文章<聊聊Netty那些事儿之从内核角度看IO模型>中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以 ...

  8. 《AlignedReID:Surpassing Human-Level Performance in Person Re-Identification》理解

  9. 动画 ---Animejs 简单使用与源码解析

    Anime是什么 Anime有什么用 Anime是作何做的 requireAnimationFrame() engine(){ // 处理让多个帧运动起来 ​ play() ​ step()} ani ...

  10. Tapdata Real Time DaaS 技术详解 PART I :实时数据同步

      摘要:企业信息化过程形成了大量的数据孤岛,这些并不连通的数据孤岛是企业数字化转型的巨大挑战.Tapdata Real Time DaaS 采用的CDC模式,具有巨大的优势,同时是一个有技术壁垒的活 ...