题目

给定一棵树,每个点有一个活动时间,长度为正整数$t_i$

你需要安排每个点的活动时间什么时候开始什么时候结束,并且满足:任何一个时刻没有两个相邻的点都在活动

开始时刻为0,在以上条件下最小化所有点的结束时间之和

$n \leq 2000$

思路

首先,给定的所有$t_i$都是正整数,说明答案一定是整数(这虽然很显然,但是很重要)

考虑某一个点什么时候开始

显然,最优的情况下它的开始时间可以被安排到和自己某个相邻点的结束时间相邻(或者它自己是在开始时刻就开始的)

又考虑到隔壁点也满足这一条,所以对于这个点来说,它的前驱时间(也就是在开始前的等待时间)一定可以被表示为从这个点开始的一条树链的时间和

注意上面的“可以被表示为”这样的表述:不是说只能这么安排,是说这样安排是可行的,而且方便我们设计算法

对于每个点$u$,计算数组$dis[u]$,表示排序去重后的从$u$出发的所有树链的长度

然后设$dp[u][i]$表示从根节点开始$dp$,在当前节点的子树中得到的结果:$dp[u][i]$表示在$u$开始之前已经经过了$dis[u][i]$这么长的等待时间

注意这里的$dp$并没有考虑父亲那边,而是只考虑了子树部分

因此在递归回到父亲那里做转移的时候,要这样操作:


t1[0]=t2[cnt[v]+1]=1e15;
for(i=1;i<=cnt[v];i++) t1[i]=min(t1[i-1],dp[v][i]);
for(i=cnt[v];i>=1;i--) t2[i]=min(t2[i+1],dp[v][i]);
l=0;r=0;
for(i=1;i<=cnt[u];i++){
while(dis[v][l+1]+w[v]<=dis[u][i]) l++;
while(dis[v][r]<dis[u][i]+w[u]) r++;
dp[u][i]+=min(t1[l],t2[r]);
}

做一个前后缀最小值,然后两边双指针进去搞到范围,然后再转移

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,first[10010],cnte=-1;
struct edge{
int to,next;
}a[20010];int w[10010];
inline void add(int u,int v){
a[++cnte]=(edge){v,first[u]};first[u]=cnte;
a[++cnte]=(edge){u,first[v]};first[v]=cnte;
}
ll dis[2010][2010],lis[2010],dp[2010][2010];
int root,cnt[2010];
void getlis(int u,int f,ll d){
lis[++cnt[root]]=d;
// cout<<"getlis "<<u<<' '<<root<<' '<<d<<'\n';
int i,v;
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==f) continue;
getlis(v,u,d+w[v]);
}
}
void init(){
memset(first,-1,sizeof(first));
int i,num,t1,t2;
n=read();
for(i=1;i<n;i++){
t1=read();t2=read();
add(t1,t2);
}
for(i=1;i<=n;i++) w[i]=read();
for(i=1;i<=n;i++){
root=i;getlis(i,0,0);
sort(lis+1,lis+cnt[i]+1);
cnt[i]=unique(lis+1,lis+cnt[i]+1)-lis-1;
memcpy(dis[i]+1,lis+1,sizeof(ll)*(cnt[i]+1));
dis[i][0]=-1e15;
dis[i][cnt[i]+1]=1e15;
}
}
void dfs(int u,int f){
int i,v,j,l,r;
for(i=1;i<=cnt[u];i++) dp[u][i]=dis[u][i]+w[u];
for(i=first[u];~i;i=a[i].next){
v=a[i].to;if(v==f) continue;
dfs(v,u);
}
for(j=first[u];~j;j=a[j].next){
v=a[j].to;if(v==f) continue;
static ll t1[2010],t2[2010];
t1[0]=t2[cnt[v]+1]=1e15;
for(i=1;i<=cnt[v];i++) t1[i]=min(t1[i-1],dp[v][i]);
for(i=cnt[v];i>=1;i--) t2[i]=min(t2[i+1],dp[v][i]);
l=0;r=0;
for(i=1;i<=cnt[u];i++){
while(dis[v][l+1]+w[v]<=dis[u][i]) l++;
while(dis[v][r]<dis[u][i]+w[u]) r++;
dp[u][i]+=min(t1[l],t2[r]);
}
}
}
int main(){
init();
dfs(1,0);
int i;ll ans=1e15;
for(i=1;i<=cnt[1];i++) ans=min(ans,dp[1][i]);
cout<<ans<<'\n';
}

Flash by sshockwave [树dp]的更多相关文章

  1. CF456D A Lot of Games (字典树+DP)

    D - A Lot of Games CF#260 Div2 D题 CF#260 Div1 B题 Codeforces Round #260 CF455B D. A Lot of Games time ...

  2. HDU4916 Count on the path(树dp??)

    这道题的题意其实有点略晦涩,定义f(a,b)为 minimum of vertices not on the path between vertices a and b. 其实它加一个minimum ...

  3. Codeforces 219D. Choosing Capital for Treeland (树dp)

    题目链接:http://codeforces.com/contest/219/problem/D 树dp //#pragma comment(linker, "/STACK:10240000 ...

  4. HDU4276 The Ghost Blows Light SPFA&&树dp

    题目的介绍以及思路完全参考了下面的博客:http://blog.csdn.net/acm_cxlove/article/details/7964739 做这道题主要是为了加强自己对SPFA的代码的训练 ...

  5. Tsinsen A1219. 采矿(陈许旻) (树链剖分,线段树 + DP)

    [题目链接] http://www.tsinsen.com/A1219 [题意] 给定一棵树,a[u][i]代表u结点分配i人的收益,可以随时改变a[u],查询(u,v)代表在u子树的所有节点,在u- ...

  6. HDU 3016 Man Down (线段树+dp)

    HDU 3016 Man Down (线段树+dp) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Ja ...

  7. bzoj 3572世界树 虚树+dp

    题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...

  8. bzoj 2286 [Sdoi2011]消耗战 虚树+dp

    题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...

  9. (纪念第一道完全自己想的树DP)CodeForces 219D Choosing Capital for Treeland

    Choosing Capital for Treeland time limit per test 3 seconds memory limit per test 256 megabytes inpu ...

随机推荐

  1. 构建ExtJS 6.x程序

    构建ExtJS 6.x程序 ExtJS也有自己的打包工具 SenchaCmd,它用来生成构建ExtJS前端组织架构,最后打包发布生产,操控着前端整个开发生命周期,SenchaCmd依赖于JDK,所以要 ...

  2. 深度解析JQuery Dom元素操作技巧

    深度解析JQuery Dom元素操作技巧 DOM是一种与浏览器.平台.语言无关的接口,使用该接口可以轻松访问页面中所有的标准组件,这篇文章给大家介绍了JQuery dom元素操作方法,写的十分的全面细 ...

  3. backtrace函数

    1.函数原型 #include <execinfo.h> int backtrace(void **buffer, int size); 该函数获取当前线程的调用堆栈,获取的信息将会被存放 ...

  4. Ubuntn14.04安装MATLAB2015b

    一部分转载自:CSDN ,其他结合自己电脑环境配置,旨做备份和记录,同时也提供一个参考. 安装环境: linux Ubuntu14.04 (x64) 软件包下载地址: Matlab R2015b_gl ...

  5. MySQL 从入门到删库

    基本操作 登陆指令 mysql -u用户名 -p密码(可以非明文输入) -h主机/IP -D端口 --prompt 提示符 修改提示符 \D 日期 \d 当前数据库 \h 服务器名 \u 用户名 // ...

  6. 暗影精灵3安装无线网卡驱动(ubuntu16.04)

    干货,无线网卡安装步骤: 1. 由于暗影精灵3的无线网卡较新,版本为Realtek Device b822,(查看命令为lspci | grep -i net,Ethernet controller代 ...

  7. django生产环境中部署

    https://www.cnblogs.com/chenice/p/6921727.html 本节内容 uwsgi 介绍 uwsgi安装使用 nginx安装配置 django with nginx 如 ...

  8. 【多校联合】(HDU6095)Rikka with Competition

    题意:给定$n$个数,代表$n$个选手的能量高低,现在再给一个$k$,任意在$n$个选手中挑取两个选手比赛,如果$|a_i−a_j|>K$,那么能量高的选手获胜,另一个将被淘汰,否则两个人都有机 ...

  9. VMware快照

    越来越多的人喜欢使用虚拟机来做实验,但是实验过程并不总是顺利的,所以我们就需要掌握虚拟机快照的使用方法,个人建议的顺序为: 1 在虚拟机打开之前,点击“虚拟机”--"快照"--&q ...

  10. 一个简单的ipfs音乐播放器的实现

    IPFS音乐播放器 IPFS相关 IPFS第一次亲密接触 什么是IPFS IPFS对比HTTP/FTP等协议的优势 IPFS应用场景 -移动数据 交易 路由 网络 定义数据 命名 使用数据 具体场景; ...