题目

给定一棵树,每个点有一个活动时间,长度为正整数$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. PHP 使用程序进行数据库字典文件生成 导出数据库字典

    作为一个程序员肯定是不愿意写文档的!!! 尤其最麻烦的数据库字典文档 所以偷懒写了一个PHP程序来进行数据库字典导出 记录一下  以免以后忘记 //使用的是Laravel框架 可以转换成原生导出$ta ...

  2. 微信小程序关于tabbar点击切换数据不刷新问题

    微信小程序中经常遇到的需求就是我提交了一个表单或者进行了一个操作,需要在我的个人中心页面中实时显示出来,但是小程序中的tabbar切换类似于tab切换 并不会进行页面刷新请求 所以总是会造成一些数据更 ...

  3. Python基于jieba的中文词云

    今日学习了python的词云技术 from os import path from wordcloud import WordCloud import matplotlib.pyplot as plt ...

  4. centos安装zabbix(server+agent)

    本文包含zabbix_server编译安装,zabbix_agent编译安装,中文字体修正 Mysql模板监控,Nginx模板监控,以及简单的web页面的使用 中文乱码的解决方案 zabbix乱码是字 ...

  5. WordPress4.9 最新版本网站安全漏洞详情与修复

    wordpress 目前互联网的市场占有率较高,许多站长以及建站公司都在使用这套开源的博客建站系统来设计网站,wordpress的优化以及html静态化,深受google以及搜索引擎的喜欢,全世界大约 ...

  6. spoj1026 favorite dice

    #include <bits/stdc++.h> using namespace std; int n,t; ; double dp[N]; /* 甩一个n面的骰子,问每一面都被甩到的需要 ...

  7. win10 无法修改默认程序 默认打开方式的解决方法

    此时是2018年11月24日 win10 pro 64位 版本是1803  具体版本号是17134 情景: 我的状况是.json文件的默认打开方式被新安装的应用霸占了,然后无论是通过“右键-属性-更改 ...

  8. CSS3 : transform 与 transform-origin 属性可以使元素样式发生转变

    CSS3 : transform 用于元素样式的转变,比如使元素发生位移.角度变化.拉伸缩小.按指定角度歪斜 transform结合transition可实现各类动画效果 transform : tr ...

  9. Python网络编程(进程池、进程间的通信)

    线程池的原理:        线程池是预先创建线程的一种技术.线程池在还没有任务到来之前,        创建一定数量的线程,放入空闲队列中.这些线程都是处于睡眠状态,        即均为启动,不消 ...

  10. hibernate 批量插入

    Session session = sessionFactoryUpLowLimit.openSession(); session.beginTransaction(); for(int i=0 ;i ...