题目大意

有一种生成\(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. ubuntu配置django

    安装 安装Apache sudo apt-get install apache2 安装Django 下载Django 安装mod_wsgi sudo apt-get install libapache ...

  2. Keras学习

    参加比赛用到了keras,虽然之前用tensorflow,但是感觉tensorflow的确不太友好,api比较难读,然后就学习keras使用 随着深入,发现keras的api确实比较友好 跑了一些ex ...

  3. GDB调试动态链接库

    http://cyukang.com/2012/06/25/gdb-with-libso.html http://cyukang.com/2011/05/06/valgrind.html

  4. angular - 新建项目 - 2

    ng new testNg 新建项目后,从网络上拉取模板(最后缓存下来,我们下次创建项目的时间将会减少80%) 安装过程中,需要我们提供Git账号和姓名 最后,我们进入 useNg 然后,启动服务器 ...

  5. favico是针对网页图标内容更改

    favico.js源码 (function () { var Favico = function (opt) { "use strict"; opt = opt ? opt : { ...

  6. Eclipse 修改字符集

    Eclipse 修改字符集 默认情况下 Eclipse 字符集为 GBK,但现在很多项目采用的是 UTF-8,这是我们就需要设置我们的 Eclipse 开发环境字符集为 UTF-8, 设置步骤如下: ...

  7. 苦逼IT才能看懂的笑话

    这是苦逼IT才能看懂的笑话1.栈和队列的区别是啥? 吃多了拉就是队列:吃多了吐就是栈 2.世界上最遥远的距离不是生与死,而是你亲手制造的BUG就在你眼前,你却怎么都找不到她... 3.<c++程 ...

  8. MyEclipse的实体关系设计

    原文地址:http://www.myeclipsecn.com/learningcenter/database-development/myeclipse-entity-relation-design ...

  9. 基于django做增删改查组件,分页器组件

    增删改查组件 一.Djangoadmin的启发 二.基于Djangoadmin实现数据的增删改查 分页器组件 分页器组件的介绍以及源码解读 补充:源码下载,

  10. cuda9,cuda8分享百度云下载

    一.文件名称: md5-cuda9cuda-repo-ubuntu1704-9-0-local_9.0.176-1_amd64.debcuda-repo-ubuntu1604-9-0-local_9. ...