UOJ #105. 【APIO2014】Beads and wires treedp

8 篇文章 0 订阅

题意

在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 11到 n。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

Append(w, v):一个新的珠子 w 和一个已经添加的珠子 v 用红线连接起来。
Insert(w, u, v):一个新的珠子 w 插入到用红线连起来的两个珠子 u,v之间。具体过程是删去 u,v 之间红线,分别用蓝线连接 u,w 和 w,v。
每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。

给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。

你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。

分析

不妨我们考虑从某个点开始,那么一定不存在一些跨越当前点的两个子树上来因为加入当前点而变成两条蓝色的边

那么我们考虑treedp,发现每次都要跨过两层点,其实我们只需要知道是不是中间的点即可, f[i][0/1] f [ i ] [ 0 / 1 ] 表示i子树内,i这个点是不是中间点的贡献

然后转移:

f[i][0]=json[i]max(f[j][0],f[j][1]+cost) f [ i ] [ 0 ] = ∑ j ∈ s o n [ i ] m a x ( f [ j ] [ 0 ] , f [ j ] [ 1 ] + c o s t )

f[i][1]=f[i][0]+maxjson[i](max(f[j][0],f[j][1]+cost)+f[j][0]+cost) f [ i ] [ 1 ] = f [ i ] [ 0 ] + m a x j ∈ s o n [ i ] ( − m a x ( f [ j ] [ 0 ] , f [ j ] [ 1 ] + c o s t ) + f [ j ] [ 0 ] + c o s t )

然后就有一个 N2 N 2 的dp了,常规的,考虑优化,只需要增加一个换跟操作

要记录某个点x从父亲过来的那条边的代价,还有以下的最大值和次大值:

(max(f[j][0],f[j][1]+cost)+f[j][0]+cost) ( − m a x ( f [ j ] [ 0 ] , f [ j ] [ 1 ] + c o s t ) + f [ j ] [ 0 ] + c o s t )

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 200010;
const ll inf = 1e9;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

struct node{ll x,y,next,d;}edge[N<<1]; ll len,first[N];
void ins(ll x,ll y,ll d){len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}

ll f[N][2] , h1[N] , h2[N] , hp1[N] , hp2[N]; ll g[N][2];
void upd(ll &x,ll y){x = x+y;}
void updh(ll x,ll y,ll d)
{
  if(h1[x] < d)
  {
    h2[x] = h1[x]; hp2[x] = hp1[x];
    h1[x] = d; hp1[x] = y;
  }
  else if(h2[x] < d)
  {
    h2[x] = d; hp2[x] = y;
  }
}
void dfs(ll x,ll fa)
{
  f[x][0] = 0; h1[x] = h2[x] = -inf;
  for(ll k=first[x];k!=-1;k=edge[k].next)
  {
    ll y = edge[k].y;
    if(y==fa) continue;
    dfs(y,x);
    upd(f[x][0] , max(f[y][0] , f[y][1] + edge[k].d) );
    updh(x , y , -max(f[y][0] , f[y][1] + edge[k].d) + f[y][0] + edge[k].d);
  }f[x][1] = f[x][0] + h1[x];
}

ll ans = 0;

void dfs2(ll x,ll fa,ll d)
{
  ans = max(ans , f[x][0] + max(g[x][0] , g[x][1] + d));
  // if(f[x][0] + max(g[x][0] , g[x][1] + d) == 142) printf("%d\n",x);
  for(ll k=first[x];k!=-1;k=edge[k].next)
  {
    ll y = edge[k].y;
    if(y==fa) continue;
    ll tmp = max(g[x][0] , g[x][1] + d);
    ll g0 = tmp + f[x][0] - max(f[y][0] , f[y][1] + edge[k].d);
    g[y][0] = g0;
    if(hp1[x] == y) g[y][1] = g0 + max(h2[x] , -tmp + g[x][0] + d);
    else g[y][1] = g0 + max(h1[x] , -tmp + g[x][0] + d);
    dfs2(y,x,edge[k].d);
  }
}

int main()
{

  ll n = read(); len = 0; memset(first,-1,sizeof(first));
  for(ll i=1;i<n;i++){ll x = read(); ll y = read(); ll d = read(); ins(x,y,d); ins(y,x,d);}

  dfs(1,0);
  g[1][0] = 0; g[1][1] = -inf; dfs2(1,0,-inf);

  return printf("%lld\n",ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值