题目描述

  给你一棵树,你要从\(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. Dockerfile centos7_tomcat7.0.64_jdk7u80

    FROM centos:7 MAINTAINER jiangzhehao WORKDIR /tmp RUN yum -y install net-tools ADD jdk-7u80-linux-x6 ...

  2. 十二、存token获取token刷新token发送header头

    //测试token //获取token function setToken(data){ var storage = window.localStorage; if(!storage){ alert( ...

  3. Mysql乱码问题总结

    这两天研究了下Mysql的字符集编码和排序规则,有个很典型的问题就是乱码问题.所以小记一下. http://www.jianshu.com/p/4c6a27542df4 http://blog.csd ...

  4. c++入门之命名空间存在的意义

    看过鸡啄米的C++编程入门系列教程的朋友,应该能注意到,在其中的很多实例中,都有这么一条语句:using namespace std;,即使用命名空间std,其作用就是规定该文件中使用的标准库函数都是 ...

  5. Python学习第十篇——函数初步

    def make_album(name,album_name,song_nums = 1): dict_album = {name:[album_name]} if int(song_nums) &g ...

  6. Bad Hair Day POJ - 3250 (单调栈入门题)

    Some of Farmer John's N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is self-cons ...

  7. Golang中进行reslice时的注意事项

    先看下面代码: package main import "fmt" func main() { slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8} ...

  8. centos7 network eno16777736

    Network service not running - eno16777736 not activated - CentOShttps://www.centos.org/forums/viewto ...

  9. 解决 linux 下面解压缩 中文文件名乱码问题的方法 unzip -O CP936

    Linux 解压缩 zip包中文目录出现乱码的问题. 出现问题如图示: unzip -O CP936 xxx.zip 用这种方式处理一下就好了.

  10. 【面试】MySQL的事务和索引

    MySQL事务 MySQL事务主要用于处理操作量大,复杂度高的数据. 比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这些数据库操作 ...