LOJ题目传送门

题目描述

在逃亡者的面前有一个迷宫,这个迷宫由 n个房间和 n−1 条双向走廊构成,每条走廊会链接不同的两个房间,所有的房间都可以通过走廊互相到达。换句话说,这是一棵树。

逃亡者会选择一个房间进入迷宫,走过若干条走廊并走出迷宫,但他永远不会走重复的走廊。

在第 i个房间里,有 Fi个铁球,每当一个人经过这个房间时,他就会受到铁球的阻挡。逃亡者手里有 V 个磁铁,当他到达一个房间时,他可以选择丢下一个磁铁(也可以不丢),将与这个房间相邻的所有房间里的铁球吸引到这个房间。这个过程如下:

逃亡者进入房间。
逃亡者丢下磁铁。
逃亡者走出房间。
铁球被吸引到这个房间。

注意逃亡者只会受到这个房间原有的铁球的阻拦,而不会受到被吸引的铁球的阻挡。

在逃亡者走出迷宫后,追逐者将会沿着逃亡者走过的路径穿过迷宫,他会碰到这条路径上所有的铁球。

请帮助逃亡者选择一条路径,使得追逐者遇到的铁球数量减去逃亡者遇到的铁球数量最大化。
输入输出格式
输入格式:

第一行两个空格隔开的整数整数 n 和 V 。 第二行 n个空格隔开的整数表示 Fi ​​。 之后的 n−1 行,每行两个空格隔开的整数 x 和 y ,表示有一条走廊连接编号为 x 和编号为 y 的房间。

输出格式:

输出一个整数表示最优情况下追逐者遇到的铁球数量减去逃亡者遇到的铁球数量。

输入输出样例
输入样例#1:

12 2
2 3 3 8 1 5 6 7 8 3 5 4
2 1
2 7
3 4
4 7
7 6
5 6
6 8
6 9
7 10
10 11
10 12

输出样例#1:

36

说明

样例解释

有一个最优方案如下:

从6号房间进入迷宫并丢下第一个磁铁,他遇到了5个铁球,这个时候6号房间会有27个铁球,而5号,7号,8号,9号房间都没有铁球。
走到7号房间丢下第二个磁铁并走出迷宫,他遇到了0个铁球,这个时候7号房间会有41个铁球,而2号,4号,6号,10号房间会没有铁球。

在这个过程中,逃亡者会遇到5个铁球而追逐者会遇到 41个铁球。

数据范围

对于 100 100% 100的数据,
有 1 ≤ n ≤ 1 0 5 ; 0 ≤ V ≤ 100 ; 0 ≤ F i ≤ 1 0 9 , 1 ≤ n ≤ 1 0 5 ; 。 1≤n≤10^5;0≤V≤100;0≤Fi≤10^9,1\le n\le 10^5;。 1≤n≤105;0≤V≤100;0≤Fi≤109,1≤n≤105;。


首先非常显然的就是,这些房间连成一棵树,而且是一棵无根树。假定点 x x x是根,那么逃亡者的路径就是从 x x x的子树里选一个点,往这个点走。然后重复上述过程,直到他选择一个点离开迷宫。
所以一个显然的树形DP就是枚举每一个点做根,然后以这个根向下DP,求出扔下 v v v个磁铁所能获得的最大铁球差异值。
当然我们需要明确,铁球的差异是如何产生的。当我们在树上 x x x点扔下一个磁铁,那么差异是这个点的所有儿子的权值和(为什么没有它自己和它父亲?因为这些点都是先走的,或者这些点被之前的点吸引了,并不可能增加当前点的贡献)。
所以这个树形DP很简单,然而复杂度是过不去的。只能拿70分。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define MAXN 100005
#define ll long long
using namespace std;
char tc(){static char fl[100000],*A=fl,*B=fl;return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
ll read(){char c;ll x=0,y=1;while(c=tc(),(c<'0'||c>'9')&&c!='-');if(c=='-') y=-1;else x=c-'0';while(c=tc(),c>='0'&&c<='9')x=x*10+c-'0';return x;
}
ll ans,f[MAXN][105];
int n,v,cnt,val[MAXN],head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],vis[MAXN];
void add(ll x,ll y){go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;go[cnt]=x;nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
ll rise(int x,int v){if(v==0) return 0;if(f[x][v]) return f[x][v];ll res=0,out=0;vis[x]=1;register int i;for(i=head[x];i!=-1;i=nxt[i]){int to=go[i];if(vis[to]) continue;res+=val[to];}for(i=head[x];i!=-1;i=nxt[i]){int to=go[i];if(vis[to]) continue;out=max(out,res+rise(to,v-1));}for(i=head[x];i!=-1;i=nxt[i]){int to=go[i];if(vis[to]) continue;out=max(out,rise(to,v));}vis[x]=0;f[x][v]=out;return f[x][v];
}
int main()
{n=read();v=read();register int i,j,k;memset(head,-1,sizeof(head));for(i=1;i<=n;i++) val[i]=read();for(i=1;i<n;i++){ll x=read(),y=read();add(x,y);}if(n<=1000){  //这个是根据数据特判for(i=1;i<=n;i++){for(j=1;j<=n;j++) vis[j]=0;for(j=1;j<=n;j++)for(k=1;k<=v;k++) f[j][k]=0;ans=max(ans,rise(i,v));}printf("%lld\n",ans);return 0;}printf("%lld",rise(1,v));  //由于保证有30分从1开始,所以直接以1为根做return 0;
}
那么正解是什么呢?
既然我们要以每一个点为根,那么我们可以考虑定1为假根,然后用一种类似点分治的思路,去计算它的儿子的答案。(实际上就是以儿子为根的答案),我们设up[x][i]表示从某点出发,扔出i个磁铁,最终到达x的最大差异值,而down[x][i]表示从x出发,扔下i个磁铁所能获得的最大差异值。
那么我们显然可以写出转移:
for(int j=1;j<=v;j++) up[x][j]=max(up[x][j],up[to][j]),down[x][j]=max(down[x][j],down[to][j]);
for(int j=1;j<=v;j++){up[x][j]=max(up[x][j],up[to][j-1]+deg[x]-val[to]);  //up从下往上,所以应该减去下面的点。down[x][j]=max(down[x][j],down[to][j-1]+deg[x]-val[fa[x]]);  //down从上往下,所以减去父亲
}
但是我们要去重,因为如果不去重,可能up和down走的是同一个儿子,那么这样就违反了题目每条边只能走一次的规定。于是我们在循环的时候先更新答案,再更新up和down,代码如下:
for(int i=head[x];i!=-1;i=nxt[i]){int to=go[i];if(fa[x]==to) continue;sta[++top]=to;  //sta这句话和目前无关,可以无视for(int j=1;j<v;j++) ans=max(ans,up[x][j]+down[to][v-j]);  //先更新,就能保证不会重复for(int j=1;j<=v;j++) up[x][j]=max(up[x][j],up[to][j]),down[x][j]=max(down[x][j],down[to][j]);for(int j=1;j<=v;j++){up[x][j]=max(up[x][j],up[to][j-1]+deg[x]-val[to]);down[x][j]=max(down[x][j],down[to][j-1]+deg[x]-val[fa[x]]);}
}
ans=max(ans,max(down[x][v],up[x][v]));
但是我们还得考虑一个问题,就是比如我们从 u u u走上 x x x,再从 x x x走下 v v v,这样的答案和从 v v v走上 x x x,再从 x x x走下 u u u的大答案是不一样的。而我们上面的统计由于为了去重,就会导致我们匹配up和down时up走的子树肯定在down左边。但如果在右边怎么办?我们把 x x x的子树顺序前后翻转一下,就可以使匹配时up走的子树在down右边。这样操作就可以统计所有的答案。
我们在代码里用一个栈来操作。上文的sta就是将所有儿子放在栈里。最后统计右边的时候就倒着枚举栈即可。
while(top){int to=sta[top--];for(int j=1;j<v;j++) ans=max(ans,up[x][j]+down[to][v-j]);for(int j=1;j<=v;j++) up[x][j]=max(up[x][j],up[to][j]),down[x][j]=max(down[x][j],down[to][j]);for(int j=1;j<=v;j++){up[x][j]=max(up[x][j],up[to][j-1]+deg[x]-val[to]);down[x][j]=max(down[x][j],down[to][j-1]+deg[x]-val[fa[x]]);}
}
ans=max(ans,max(down[x][v],up[x][v]));
下面是完整的代码:
#include<bits/stdc++.h>
#define MAXN 100005
#define ll long long
using namespace std;
int read(){char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,v,cnt,val[MAXN],head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],fa[MAXN],sta[MAXN],top;
ll up[MAXN][105],down[MAXN][105],ans,deg[MAXN];
void add(int x,int y){go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;go[cnt]=x;nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dfs(int x){for(int i=1;i<=v;i++) up[x][i]=deg[x],down[x][i]=deg[x]-val[fa[x]];for(int i=head[x];i!=-1;i=nxt[i]){int to=go[i];if(fa[x]==to) continue;fa[to]=x;dfs(to);}for(int i=head[x];i!=-1;i=nxt[i]){int to=go[i];if(fa[x]==to) continue;sta[++top]=to;for(int j=1;j<v;j++) ans=max(ans,up[x][j]+down[to][v-j]);for(int j=1;j<=v;j++) up[x][j]=max(up[x][j],up[to][j]),down[x][j]=max(down[x][j],down[to][j]);for(int j=1;j<=v;j++){up[x][j]=max(up[x][j],up[to][j-1]+deg[x]-val[to]);down[x][j]=max(down[x][j],down[to][j-1]+deg[x]-val[fa[x]]);}}ans=max(ans,max(down[x][v],up[x][v]));for(int i=1;i<=v;i++) up[x][i]=deg[x],down[x][i]=deg[x]-val[fa[x]];while(top){int to=sta[top--];for(int j=1;j<v;j++) ans=max(ans,up[x][j]+down[to][v-j]);for(int j=1;j<=v;j++) up[x][j]=max(up[x][j],up[to][j]),down[x][j]=max(down[x][j],down[to][j]);for(int j=1;j<=v;j++){up[x][j]=max(up[x][j],up[to][j-1]+deg[x]-val[to]);down[x][j]=max(down[x][j],down[to][j-1]+deg[x]-val[fa[x]]);}}ans=max(ans,max(down[x][v],up[x][v]));
}
int main()
{n=read();v=read();memset(head,-1,sizeof(head));for(int i=1;i<=n;i++) val[i]=read();for(int i=1;i<n;i++){int x=read(),y=read();add(x,y);deg[x]+=val[y];deg[y]+=val[x];}dfs(1);printf("%lld",ans);return 0;
}

CEOI2017Chase追逐——树形DP相关推荐

  1. 【洛谷4657】[CEOI2017] Chase(一个玄学的树形DP)

    点此看题面 大致题意: 有一棵树,树上编号为 i i i的节点上有 F i F_i Fi​个铁球,逃亡者有 V V V个磁铁,当他在某个节点放下磁铁时,与这个节点相邻的所有节点上的铁球都会被吸引到这个 ...

  2. BNUOJ 52305 Around the World 树形dp

    题目链接: https://www.bnuoj.com/v3/problem_show.php?pid=52305 Around the World Time Limit: 20000msMemory ...

  3. [树形dp] Jzoj P5233 概率博弈

    Description 小A和小B在玩游戏.这个游戏是这样的: 有一棵n个点的以1为根的有根树,叶子有权值.假设有m个叶子,那么树上每个叶子的权值序列就是一个1->m 的排列. 一开始在1号点有 ...

  4. fwt优化+树形DP HDU 5909

    1 //fwt优化+树形DP HDU 5909 2 //见官方题解 3 // BestCoder Round #88 http://bestcoder.hdu.edu.cn/ 4 5 #include ...

  5. BZOJ 1040 ZJOI2008 骑士 树形DP

    题目大意:给定一个基环树林,每一个点上有权值,要求选择一个权值和最大的点集,要求点集中的随意两个点之间不能直接相连 最大点独立集--考虑到n<=100W,网络流铁定跑不了,于是我们考虑树形DP ...

  6. POJ 3342 树形DP+Hash

    这是很久很久以前做的一道题,可惜当时WA了一页以后放弃了. 今天我又重新捡了起来.(哈哈1A了) 题意: 没有上司的舞会+判重 思路: hash一下+树形DP 题目中给的人名hash到数字,再进行运算 ...

  7. [NC15748]旅游 树形dp基础

    菜鸡第一次接触树形dp这个东西,不过这个东西还是很好理解的(可能是因为模板题吧) 个人感觉,相比线性dp,树形dp的状态转移方程更加的直观,难点主要是在"树"的结构上比较麻烦. 题 ...

  8. 容斥 + 树形dp ---- 2021 icpc 沈阳 L Perfect Matchings

    题目链接 题目大意: 就是给你一个2n2n2n个点的完全图,从这个图里面抽出2n−12n-12n−1条边,这些边形成一颗树,现在问你剩下的图里面点进行完美匹配有多少种方案? 解题思路: 一开始被完美匹 ...

  9. 树形dp ---- gym101667 A(贪心 + 树形dp + 两个dp方程组维护)

    题目链接 题目大意: 就是一棵5e35e35e3的树,可以选择一些点,放上基站,如果uuu上的基站价值为ddd,那么距离uuu小于等于ddd的点都会被覆盖,问使得整棵树被覆盖需要的最小价值. 解题思路 ...

最新文章

  1. vs2008中,在OCX控件中应用doc/view基本步骤
  2. 写注册机犯法吗_逼着一个受害者去向另一个受害者道歉,不过分吗?
  3. goodFeaturesToTrack函数
  4. c++ 双端队列 deque 之 (头部、尾部)插入元素/删除元素/
  5. MaxCompute与OSS非结构化数据读写互通(及图像处理实例)
  6. spring下jndi配置
  7. Power Query 系列 (06) - M 语言结构化数据类型
  8. 艾伦·凯(Alan Kay)的深刻见解
  9. redis下载安装教程
  10. javaWeb随机生成网页验证码图片
  11. 002HTML常用标签
  12. 2022茶艺师(初级)试题及在线模拟考试
  13. 快商通对话式AI打造超级咨询师获中科院《互联网周刊》高度认可
  14. linux制作xp u盘启动盘,ultraiso制作u盘启动盘linuxu大侠u盘装xp
  15. MATLAB强化学习实战(十一) 使用自定义训练循环训练强化学习策略
  16. 【红外技术】红外焦平面阵列非均匀性校正
  17. 常用的表格正则验证 + 省份选择 JS JQ
  18. 大家都在用的抠图换背景软件,这些软件你知道几个?
  19. 用友盟社会化组件,分享到微信和新浪微博
  20. 漏洞复现-Redis

热门文章

  1. 一个不像程序员的程序员,褚霸:IT男得有品位才能做出极致的产品
  2. mapstruct在eclipse生成不了mapper的实现类的问题
  3. html字段最大长度限制,html input 限制输入的长度并提示的方法
  4. 主要厂商产品特点、产品规格、价格、销量、销售收入-全球与中国分布式天线系统设备
  5. (一)调整段落、段前、段后间距
  6. 英知汇——他们劝他们不要借钱却愿意借钱给他们,绕吗?
  7. 用计算机来弹下山,森林报测试题大全及答案
  8. Spring属性注入的多种方式
  9. 地球上的模型,避免破面现象,需要加参考点
  10. 《浮生六记》,想娶半个陈芸