题目大意

有一种生成\(n\)个点的树的方法为:

一开始有一个点,\(n-1\)次操作,每次可以有两种操作:1.选一个点,用一条红边将它与新点连接;2.将新点放在一条红边上,新点与这条红边两端点直接的连边变成蓝色

给出一个\(n(n\leq 2\times10^5)\)个点的树,问如何分配边的颜色使这棵树能用上述方法生成,而且蓝边权值之和最大

题解

(想要简明且优秀的题解点这里

考虑那种生成方法,发现每条蓝边肯定是由一条红边“分裂”的,那就可以把“连红边”和“分裂红边”放在一起考虑

将初始点当根,那么想要生成蓝边时,一定会生成一条长度为2的蓝色直链

即确定根之后,存在一种方案将所有蓝边划分成若干条长为2的直链

考虑换根dp,发现不想写

先随便找一个点当“假根”

发现在考虑点\(u\)时,根具体在哪不影响答案,但根是在 \(u\)除自己以外的子树中 还是 在\(u\)或\(u\)的子树外 会影响过点\(u\)的蓝直链形态

\(u\)是否是蓝直链的中点也会影响

那么就可以设\(f(u,i,j)\)表示考虑点\(u\)时,根在/不在\(u\)除自己以外的子树中,且\(u\)是/不是中点

又发现当根在\(u\)除自己以外的子树中,且\(u\)为中点,且过它的蓝直链在假根看来是弯链,这样它的转移就会和 过它的蓝直链在假根看来是弯链 不同

所以\(u\)就会有5个状态:\(f(u,0,0),f(u,0,1),f(u,1,0),f(u,1,1)\)(过它的直链在假根看来是弯链)\(,f(u,1,2)\)(过它的直链在假根看来是直链)

多乎哉?不多也

下面考虑如何得到\(f(u,i,j)\)

发现得到\(f(u,1,1)\)需要像儿子中连2条作为过它的蓝直链的边,\(f(u,0,1),f(u,1,2)\)需要连1条作为过它的蓝直链的边,那就需要把“实际连了几条边”记在状态里

当根在\(u\)除自己以外的子树中时,还要记当前是否有根出现在儿子的子树中

所以为了求出\(f(u,i,j)\),要再给\(u\)设\(g(u,i,j,k)\),其中\(k\)表示是否已经有根出现在子树中:\(g(u,0,0,k)\),\(g(u,0,1,k)\),\(g(u,0,2,k)\),\(g(u,1,0,k)\),\(g(u,1,1,k)\),\(g(u,1,2,k)\),\(g(u,1,3,k)\),\(g(u,1,4,k)\),\(g(u,1,5,k)\)

其中:

\(g(u,0,0,k)\)同\(f(u,0,0)\)

\(g(u,0,1,k),g(u,0,2,k)\)表示想要达到\(f(u,0,1)\)的状态,实际连了0或1条作为过它的蓝直链的边

\(g(u,1,0,k)\)同\(f(u,1,0)\)

\(g(u,1,1,k),g(u,1,2,k),g(u,1,3,k)\)表示想要达到\(f(u,1,1)\)的状态,实际连了0或1或2条作为过它的蓝直链的边

\(g(u,1,4,k),g(u,1,5,k)\)表示想要达到\(f(u,1,2)\)的状态,实际连了0或1条作为过它的蓝直链的边

多乎哉?不多也

接下来考虑转移,设\(v\)为当前考虑到的\(u\)的儿子

要注意的是,\(v\)作为中点且过\(v\)的蓝直链在假根看来也是直链时,\(u,v\)之间必须为蓝边

如果\(u,v\)都不是中点,那么它们之间的边不能为红

具体转移见代码

最后的答案是\(max( f(假根,0, 0), f(假根, 1, 0), f(假根, 1, 1) )\),因为假根没有父亲,而\(f(u,0,1),f(u,1,2)\)需要\(u\)与\(u\)的父亲之间连蓝边

代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define maxn 200010
#define maxm (maxn<<1)
#define inf 2147483647
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('\n');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
return;
}
int n,g[3][6][2],t[3][6][2],f[maxn][2][3],fir[maxn],v[maxm],nxt[maxm],cnt,w[maxm];
void ade(int u1,int v1,int w1){v[cnt]=v1,nxt[cnt]=fir[u1],w[cnt]=w1,fir[u1]=cnt++;}
int add(int x,int y){if(x==-inf||y==-inf)return -inf;return x+y;}
#define upd(x,y) x=max(x,y)
void getans(int u,int fa)
{
view(u,k)if(v[k]!=fa)getans(v[k],u);
rep(i,0,1)rep(j,0,5)rep(k,0,1)g[i][j][k]=-inf;
g[0][0][0]=g[0][1][0]=g[1][0][0]=g[1][1][0]=g[1][4][0]=0;
view(u,k)if(v[k]!=fa)
{
rep(i,0,2)t[0][i][0]=g[0][i][0];
rep(i,0,5)rep(l,0,1)t[1][i][l]=g[1][i][l];
//一、边(u,v)为红:
//1.根不在v子树中
rep(i,0,2)upd(g[0][i][0],add(t[0][i][0],f[v[k]][0][0]));
rep(i,0,5)rep(l,0,1)upd(g[1][i][l],add(t[1][i][l],f[v[k]][0][0]));
//2.根在v子树中
//当过u的蓝直链在假根看来是直链时,在v中的根看来一定是弯链,所以没有到g(u,1,1/2/3/4/5,0/1)的转移
upd(g[1][0][1],add(t[1][0][0],max(f[v[k]][1][1],f[v[k]][1][0])));
//二、边(u,v)为蓝
//1.根不在v子树中
rep(i,0,2)upd(g[0][i][0],add(add(t[0][i][0],f[v[k]][0][1]),w[k]));
rep(i,0,5)rep(l,0,1)upd(g[1][i][l],add(add(t[1][i][l],f[v[k]][0][1]),w[k]));
rep(l,0,1)
{
upd(g[0][2][l],add(add(t[0][1][l],f[v[k]][0][0]),w[k]));
upd(g[1][2][l],add(add(t[1][1][l],f[v[k]][0][0]),w[k]));
upd(g[1][3][l],add(add(t[1][2][l],f[v[k]][0][0]),w[k]));
upd(g[1][5][l],add(add(t[1][4][l],f[v[k]][0][0]),w[k]));
}
// 2.根在v子树中
upd(g[1][0][1],add(add(t[1][0][0],f[v[k]][1][2]),w[k]));
upd(g[1][2][1],add(add(t[1][1][0],max(f[v[k]][1][0],f[v[k]][1][1])),w[k]));
upd(g[1][3][1],add(add(t[1][2][0],max(f[v[k]][1][0],f[v[k]][1][1])),w[k]));
upd(g[1][5][1],add(add(t[1][4][0],max(f[v[k]][1][0],f[v[k]][1][1])),w[k]));
}
f[u][0][0]=g[0][0][0],f[u][0][1]=g[0][2][0],f[u][1][0]=max(0,g[1][0][1]),
//f(u,1,0)实际意义是“根在u子树中除u以外的区域时的答案” (即u没儿子时为-inf)
//但当它转移到u的父亲时,它的意义是 “根在u子树中时的答案”(即u没儿子时为0)
//而且最终答案最小值为0,所以在这里将它改为0不影响统计答案
f[u][1][1]=g[1][3][1],f[u][1][2]=g[1][5][1];
}
int main()
{
memset(fir,-1,sizeof(fir));
n=read();
rep(i,1,n-1){int x=read(),y=read(),z=read();ade(x,y,z),ade(y,x,z);}
getans(1,0);
write(max(f[1][0][0],max(f[1][1][0],f[1][1][1])));
return 0;
}
/*
5
1 2 10
1 3 40
1 4 15
1 5 20
*/

一些感想

换根dp可以记一位状态表示“真的根在不在该子树里”。

并不对劲的bzoj3677:p3647:[APIO2014]连珠线的更多相关文章

  1. 【BZOJ3677】[Apio2014]连珠线 换根DP

    [BZOJ3677][Apio2014]连珠线 Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色 ...

  2. Luogu P3647 [APIO2014]连珠线

    题目 换根dp. 显然对于给定的一棵有根树,蓝线都不能拐弯. 设\(f_{u,0}\)表示\(u\)不是蓝线中点时子树内的答案,\(f_{u,1}\)表示\(u\)是蓝线中点时子树内的答案.(以\(1 ...

  3. 洛谷$P3647\ [APIO2014]$连珠线 换根$dp$

    正解:换根$dp$ 解题报告: 传送门! 谁能想到$9102$年了$gql$居然还没写过换根$dp$呢,,,$/kel$ 考虑固定了从哪个点开始之后,以这个点作为根,蓝线只可能是直上直下的,形如&qu ...

  4. [换根DP]luogu P3647 [APIO2014]连珠线

    题面 https://www.luogu.com.cn/problem/P3647 不重复地取树中相邻的两条边,每次得分为两条边权和,问最大得分 分析 容易想到状态 f[i][0/1] 分别表示 i ...

  5. 洛谷 P3647 [APIO2014]连珠线(换根 dp)

    题面传送门 题意: 桌子上有 \(1\) 个珠子,你要进行 \(n-1\) 次操作,每次操作有以下两种类型: 拿出一个新珠子,并选择一个桌子上的珠子,在它们之间连一条红线 选择两个由红线相连的珠子 \ ...

  6. [Bzoj3677][Apio2014]连珠线(树形dp)

    3677: [Apio2014]连珠线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 434  Solved: 270[Submit][Status] ...

  7. 【LG3647】[APIO2014]连珠线

    [LG3647][APIO2014]连珠线 题面 洛谷 题解 首先考虑一下蓝线连起来的情况,一定是儿子-父亲-另一个儿子或者是儿子-父亲-父亲的父亲. 而因为一开始只有一个点在当前局面上,将一条红边变 ...

  8. 题解 [APIO2014]连珠线

    题解 [APIO2014]连珠线 题面 解析 首先这连成的是一棵树啊. 并且\(yy\)一下,如果钦定一个根, 那么这上面的蓝线都是爸爸->儿子->孙子这样的,因为像下图这样的构造不出来: ...

  9. bzoj3677: [Apio2014]连珠线

    Description 在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”.不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色.游戏 开始时,只有1个珠子,而接下来新的 ...

随机推荐

  1. iOS绘图—— UIBezierPath 和 Core Graphics

    前言 iOS系统本身提供了两套绘图的框架,即UIBezierPath 和 Core Graphics.而前者所属UIKit,其实是对Core Graphics框架关于path的进一步封装,所以使用起来 ...

  2. DIV浮动IE文本产生3象素的bug

    描写叙述:DIV浮动IE文本产生3象素的bug    左边对象浮动.右边採用外补丁的左边距来定位,右边对象(div)会离左边有3px的间距 复现:在开发者工具里把文本模式设置了杂项后会出现3像素的bu ...

  3. Headroom.js插件用法

    一.Headroom.js是什么? Headroom.js是一个轻量级.高性能的JS小工具(不依赖不论什么工具库.),它能在页面滚动时做出响应. 此页面顶部的导航条就是一个鲜活的案例,当页面向下滚动时 ...

  4. [Algorithms] Quicksort algorithm using TypeScript

    Quicksort (also called partition sort and pivot sort) is arguably the most used sorting algorithm. I ...

  5. SWTBOK測试实践系列(5) -- 项目中使用手动和自己主动化的策略

    手动測试和自己主动化測试永远是一个非常热门的话题.自己主动化也一直被人们捧上神坛.自己主动化測试和手动測试从技术上来说本质事实上都是測试用例设计.仅仅只是终于形式一个是人工运行,一个是代码运行罢了.这 ...

  6. Laravel建站04--建立后台文章管理

    路由配置 Route::group(['middleware' => 'auth', 'namespace' => 'Admin', 'prefix' => 'admin'], fu ...

  7. 关于global和$GLOBALS[]的一道经典面试题

    在不执行程序的情况下,你觉得的输出结果是什么? <?php $var1 = 1; $var2 = 2; function test(){ global $var1,$var2; $var2 = ...

  8. Django1.11.4中文文档

    Django管理站点¶ 自动管理界面是Django最强大的部分之一.它从您的模型中读取元数据,以提供一个快速,以模型为中心的界面,让受信任的用户可以管理您网站上的内容.管理员建议的使用仅限于组织的内部 ...

  9. MyBatis学习(二):与Spring整合(非注解方式配置MyBatis)

    搭建SpringMVC的-->传送门<-- 一.环境搭建: 目录结构: 引用的JAR包: 如果是Maven搭建的话,pom.xml的配置如下: <?xml version=" ...

  10. html抽取文本信息-java版(适合lucene建立索引)

    import org.htmlparser.NodeFilter; import org.htmlparser.Parser; import org.htmlparser.beans.StringBe ...