题目大意

有一种生成\(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. SilverLight: 数据绑定(1)-绑定到数据对象

    ylbtech-SilverLight-DataBinding: Binding to Data Objects(绑定到数据对象) 1.A, Building  a Data Object(创建一个数 ...

  2. 拦截器及 Spring MVC 整合

    一.实验介绍 1.1 实验内容 本节课程主要利用 Spring MVC 框架实现拦截器以及 Spring MVC 框架的整合. 1.2 实验知识点 Spring MVC 框架 拦截器 1.3 实验环境 ...

  3. 关于Gradle配置的小结

    前言 使用 Android Studio 来开发 Android 工程的过程中,接触 Gradle 是不可避免的,比如配置签名.引入依赖等.那么 Gradle 到底是什么东西呢? Gradle 是一个 ...

  4. AngularJS的ng-class示例

    程序下载:https://files.cnblogs.com/files/xiandedanteng/angularJSRender.rar 代码: <!DOCTYPE HTML PUBLIC ...

  5. update tableView contenSize

    NSIndexPath *messageIndexPath = [NSIndexPath indexPathForRow:afterRowCount-1 inSection:0];    [self. ...

  6. java web 站点头像上传处理 (springmvc +bootstrap+cropper)

    制作头像上传.请依据您的实际需求.改动代码,不全然正确.仅供參考! 前端页面设计使用bootstrap ,头像预览和剪裁工具使用cropper 后台使用springmvc. 如今来看前端的页面设计 前 ...

  7. mysql 查看当前连接数

    http://www.cnblogs.com/pcdelphi/archive/2009/10/31/2017990.html   实战经验: >登录到mysql数据库的终端 >show ...

  8. [转] git clone 远程分支

    git clone只能clone远程库的master分支,无法clone所有分支,解决办法如下: 找一个干净目录,假设是git_work cd git_work git clone http://my ...

  9. 文件另存为——Autocad.doc.SaveAs

    一.前言 使用pyautocad编辑好cad图纸后,往往涉及到一个保存的问题,但是官方文档并未提及,所以只能自己来了,测试了好久,终于是找到了保存的命令和参数说明. 二.方法介绍 Autocad.do ...

  10. linux c 网络编程:用域名获取IP地址或者用IP获取域名 网络地址转换成整型 主机字符顺序与网络字节顺序的转换

    用域名获取IP地址或者用IP获取域名 #include<stdio.h> #include<sys/socket.h> #include<netdb.h> int ...