题目描述

  给你一棵树,你要从\(1\)号点出发,经过这棵树的每条边至少一次,最后回到\(1\)号点,经过一条边要花费\(w_i\)的时间。

  你还可以乘车,从一个点取另一个点,需要花费\(c\)的时间。

  你最多做\(k\)次车。

  问最短时间。

  \(k\leq n\leq 20000,w,c\leq 50000\)

题解

  我们考虑把最终路线中坐车的部分替换成走路。

  那么显然不会经过一条边超过两次。

  但是每条边都要经过者少一次,所以每条边只能被一个坐车的路线覆盖。

  所以我们要选择不超过\(k\)条不相交的链,把这些链用\(c\)的代价覆盖掉。

  可以用树上背包做。

  时间复杂度:\(O(nk)\)

  还有有一个网络流的做法:每次找树上最长链,然后用\(c\)的代价覆盖掉,即把路径上的边权取反。

  正解:

  如果我们可以调整乘车的代价,并把乘车次数设为无限次,那么当最优方案的乘车次数不超过\(k\)时最优方案的路线就是最优路线。

  这个东西可以用一次树型DP解决。

  设\(f_i\)为从\(i\)开始遍历以\(i\)为根的子树并回到\(i\)的最小代价和乘车次数,\(g_i\)为从\(i\)开始遍历以\(i\)为根的子树并乘车回到\(i\)的最小代价和乘车次数。

  然后随便DP一下就行了。

  可以观察到,乘车次数是随着乘车代价单调下降的(可能是非连续的),所以可以二分乘车代价,得到答案。

  时间复杂度:\(O(n\log nw)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
using namespace std;
typedef unsigned un;
typedef pair<un,un> pii;
const int inf=0x3fffffff;
vector<pii> t[100010];
int n,k,c;
un cost;
pii f[100010];
pii g[100010];
pii operator +(pii a,pii b)
{
return pii(a.first+b.first,a.second+b.second);
}
pii operator -(pii a,pii b)
{
return pii(a.first-b.first,a.second-b.second);
}
void dp(int u,int fa)
{
f[u]=pii(0,0);
g[u]=pii(cost,1);
for(auto a:t[u])
if(a.first!=fa)
{
int v=a.first;
int w=a.second;
dp(v,u);
pii f1=pii(inf,0);
pii g1=pii(inf,0); f1=min(f1,f[u]+f[v]+pii(w,0));
f1=min(f1,f[u]+f[v]+pii(cost,1));
f1=min(f1,f[u]+g[v]);
f1=min(f1,g[u]+f[v]);
f1=min(f1,g[u]+g[v]-pii(cost,1)); g1=min(g1,f[u]+f[v]+pii(cost,1));
g1=min(g1,f[u]+g[v]);
g1=min(g1,g[u]+f[v]+pii(w,0));
g1=min(g1,g[u]+g[v]); f[u]=f1;
g[u]=g1;
}
}
void solve()
{
for(int i=1;i<=n;i++)
t[i].clear();
int x,y,z;
int sum=0;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
x++;
y++;
t[x].push_back(pii(y,z));
t[y].push_back(pii(x,z));
sum+=z;
}
cost=c;
dp(1,0);
if(f[1].second<=k)
{
printf("%d\n",sum+f[1].first);
return;
}
int l=0,r=inf;
while(l<r)
{
cost=(l+r)>>1;
dp(1,0);
if(f[1].second>k)
l=cost+1;
else
r=cost;
}
cost=l;
dp(1,0);
int ans=f[1].first-k*(l-c)+sum;
printf("%d\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
while(~scanf("%d%d%d",&n,&k,&c))
solve();
return 0;
}

【XSY1905】【XSY2761】新访问计划 二分 树型DP的更多相关文章

  1. BZOJ 1564 :[NOI2009]二叉查找树(树型DP)

    二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值,每个结 ...

  2. 【POJ 2486】 Apple Tree(树型dp)

    [POJ 2486] Apple Tree(树型dp) Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8981   Acce ...

  3. BZOJ 2286 消耗战 - 虚树 + 树型dp

    传送门 题目大意: 每次给出k个特殊点,回答将这些特殊点与根节点断开至少需要多少代价. 题目分析: 虚树入门 + 树型dp: 刚刚学习完虚树(好文),就来这道入门题签个到. 虚树就是将树中的一些关键点 ...

  4. 初学树型dp

    树型DP DFS的回溯是树形DP的重点以及核心,当回溯结束后,root的子树已经被遍历完并处理完了.这便是树形DP的最重要的特点 自己认为应该注意的点 好多人都说在更新当前节点时,它的儿子结点都给更新 ...

  5. ACM之路(13)—— 树型dp

    最近刷了一套(5题)的树型dp题目:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=116767#overview,算是入了个门,做下总结. ...

  6. 【题解】Luogu p2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat 树型dp

    题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of cours ...

  7. POJ3659 Cell Phone Network(树上最小支配集:树型DP)

    题目求一棵树的最小支配数. 支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻. 听说即使是二分图,最小支配集的求解也是还没多项式算法的.而树上求最小支配集树型DP就OK了. ...

  8. POJ 3342 - Party at Hali-Bula 树型DP+最优解唯一性判断

    好久没写树型dp了...以前都是先找到叶子节点.用队列维护来做的...这次学着vector动态数组+DFS回朔的方法..感觉思路更加的清晰... 关于题目的第一问...能邀请到的最多人数..so ea ...

  9. 洛谷P3354 Riv河流 [IOI2005] 树型dp

    正解:树型dp 解题报告: 传送门! 简要题意:有棵树,每个节点有个权值w,要求选k个节点,最大化∑dis*w,其中如果某个节点到根的路径上选了别的节点,dis指的是到达那个节点的距离 首先这个一看就 ...

随机推荐

  1. OO生存指.....抱歉无法生存

    还记得前三次的设计策略:星期二之前实现功能,星期三找一下可能出现的小bug. 这三次以及变成了:星期二之前能跑出来就行. 总体来说设计策略是:先让几个线程能够顺利运行,再开始实现功能. 在接触到多线程 ...

  2. Python_生产者消费者模型、管道、数据共享、进程池

    1.生产者消费者模型 生产者 —— 生产数据的人 消费者 —— 消费数据的人 生产者消费者模型:供销数据不平衡的现象. import time import random from multiproc ...

  3. 【Python3练习题 002】企业发放的奖金根据利润提成

    # [Python练习题 002]企业发放的奖金根据利润提成.# 利润(I)低于或等于10万元时,奖金可提10%:利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分, ...

  4. SQL server 生成数据字典

    Set nocount on ), ) DECLARE Tbls CURSOR FOR ),isnull(g.[value],'-')) AS TABLE_COMMENT FROM INFORMATI ...

  5. js去除数组重复成员

    js去除数组重复成员 第一种思路是:遍历要删除的数组arr, 把元素分别放入另一个数组tmp中,在判断该元素在arr中不存在才允许放入tmp中 用到两个函数:for ...in 和 indexOf() ...

  6. Java 简单的登录验证码

    1 验证码的作用 验证码是为了区分人与机器,如果没有验证码机制,web网站或者应用会遇到很多问题,具体如下: ① 网站容易被暴力登录攻破密码,可以制作一个自动程序不断的尝试登录,密码很容易被破解,系统 ...

  7. 11 The superlative

    1 最高级用来表明三个或更多事物之间的关系.最高级是通过在形容词之前加 "the" 并在之后加 "-est",或在形容词之前加 "the most&q ...

  8. [转帖] 百度知道: KMS 和OSPP

    https://zhidao.baidu.com/question/1819332749671662308.html Key Management Service (KMS).目前Windows Se ...

  9. 查找文献的BibTex

    BibTex可以通过Google Scholar来查找. 注意,默认情况下,Google scholar 关闭了显示BibTex链接. 打开Google Scholar 选择右上角菜单按钮 选择set ...

  10. php foreach跳出本次/当前循环与终止循环方法

    continue:跳出本次循环 break:终止循环 exit:用来结束程序执行 return: 用来结束一段代码     $arr= array('le','yang','jun','lecode' ...