[Codeforces743D][luogu CF743D]Chloe and pleasant prizes[树状DP入门][毒瘤数据]
这个题的数据真的很毒瘤,身为一个交了8遍的蒟蒻的呐喊(嘤嘤嘤)
个人认为作为一个树状DP的入门题十分合适,同时建议做完这个题之后再去做一下这个题 选课
同时在这里挂一个选取节点型树形DP的状态转移方程:
for(int i=0;i<a[rt].size();i++) { int j=a[rt][i]; dp(j); f[rt][0]+=max(f[j][0],f[j][1]); f[rt][1]+=f[j][0]; }
(PS. j 为 i 的孩子)
f[i][0]表示不选i
f[i][1]表示选i
使用一个vector来表明rt的所有孩子
题目描述:
Chloe和Vladik参加的奥林匹克运动会的慷慨赞助商允许所有参与者自己选择奖品。圣诞节即将到来,所以赞助商决定用他们的奖品装饰圣诞树。
他们给参赛者拿了N个奖品,并在每个奖品上写上一个唯一的ID(从1 1到N的整数)。
礼物i i的特点是整数ai--礼物的愉悦。礼物的愉悦可以是积极的、消极的或零的。赞助商把礼物11放在树顶上。所有其他礼物都挂在一根绳子上,绑在其他礼物上,这样每个礼物都挂在第一个礼物上,可能有一系列绳子和其他礼物。形式上,礼物形成了一个有n个顶点的树根。
礼物的愉悦。礼物的愉悦可以是积极的、消极的或零的。赞助商把礼物11放在树顶上。所有其他礼物都挂在一根绳子上,绑在其他礼物上,这样每个礼物都挂在第一个礼物上,可能有一系列绳子和其他礼物。形式上,礼物形成了一个有n个顶点的树根。
颁奖程序是这样的:参加者一个接一个地来到树上,从剩下的礼物中选择一个,剪断这个奖品挂着的绳子。请注意,所有用于在所选奖品上悬挂其他奖品的绳子都不会被割断。所以参赛者会得到所选的礼物以及所有挂在上面的礼物,可能还有一系列绳子和其他礼物。
我们的朋友,克洛伊和弗拉基克,分享了奥运会的第一名,他们将同时选择奖品!为了不打架,他们决定选择两种不同的礼物,这样挂在上面的礼物就不会相互交叉。换句话说,不应该有任何礼物挂在克洛伊选择的礼物和弗拉基克选择的礼物上。从所有可能的变种中,他们将选择这样的一对奖品,即他们在剪断绳子后所得到的所有礼物的快乐之和尽可能大。
打印Vladik和Chloe能得到的最大快感。如果他们不可能在不打架的情况下选择礼物,那么就不可能打印出来。
输入输出格式:
第一行包含一个整数n(1<=n<=2.10^5)-礼物数量
下一行包含n个整数a1,a2,…,an,(-10^9<=ai<=10^9)-礼物的愉悦
下一行(n-1)分别包含两个数字。这些行中的第i行包含整数u i和v i(1<=ui,vi<=n)-树边缘的描述。这意味着带有数字ui和vi的礼物通过绳子相互连接。绳子描述中的礼物ID可以按任意顺序给出:vi挂在ui上,ui挂在vi上
保证所有的礼物都挂在第一个礼物上,可能有一系列绳子和其他礼物。
一定要开long long!
下面开始解析:
这个题其实就是一个裸的树状DP;
但是这个题的实现的思想就是从下到上每个节点的dp值是以该节点为原始节点的最大值
这就需要我们从下到上每一个点的子树和都要求出来,然后我们再在这一个节点的所有子节点与这个点进行一次取最大值
最后我们针对于根节点求最大值就行;
个人认为是一个树状DP的入门题 (光说不做谁都会嘤嘤嘤)
下面上蒟蒻丑陋的代码:
(这个题dalao说可以有一种跑得更快的实现,不过本蒟蒻懒,就没有再写一遍,凑活着看吧qwq)
#include<iostream> #include<cstdio> #include<vector> #include<cmath> #include<algorithm> #include<set> #include<map> #define inf 2147483647888888 #define ll long long using namespace std; vector<ll> a[200866]; //使用vector数组来储存节点,更方便于表达 ll b[200866]; ll f[200866]; //中转 ll g[200866],x,y,n,ans=-inf; //最开始不要忘了要将ans重置为-inf void dfs(ll fa,ll n) { if(a[n].size()==1&&n!=1) //如果递归到了边界就退出递归 { g[n]=b[n]; //不要忘了这一步qwq return ; } ll sum=b[n]; //选取这个点 for(ll i=0;i<a[n].size();i++) //遍历这个点的所有孩子,在这一些操作中选取 { ll pos=a[n][i]; if(pos==fa) continue; dfs(n,pos); sum+=g[pos]; //sum 和g起到了一种中转同时也保证了原图中的变量不发生变化 } g[n]=sum; } void GetDeep(ll fa,ll n) { if(a[n].size()==1&&n!=1) //如果递归到了边界就退出 { f[n]=g[n]; return ; } f[n]=g[n]; ll maxn1=-inf,maxn2=-inf,sum=0; //这之间maxn2其实是起到了一个中转的作用,不要忘了初始化 for(ll i=0;i<a[n].size();i++) { ll pos=a[n][i]; if(pos==fa) //假如已经到达,就退出 continue; GetDeep(n,pos); //针对于下一层进行操作 if(f[pos]>maxn1) //如果这个点的下一个 { maxn2=maxn1; // 这个地方我调了半小时 maxn1=f[pos]; } else maxn2=max(maxn2,f[pos]); f[n]=max(f[n],f[pos]); } ans=max(ans,maxn1+maxn2); //这个地方就是一个求最大值 } int main() { scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lld",&b[i]); for(ll i=0;i<n-1;i++) { scanf("%lld%lld",&x,&y); //这里我用了一个vector来储存变量,这样便可以更好地实现储存 a[y].push_back(x); //两个push的操作 a[x].push_back(y); } dfs(0,1); GetDeep(0,1); if(ans<-2147483647) //这个地方其实是一个很玄学的优化,因为我初始置ans==-inf的时候会发现它这个点的初始值假如在没有更改的地方也会有一定的更改 //但是它的初始值一定不会更改地很大 { printf("Impossible"); return 0; } printf("%lld",ans); return 0; }
总结:树状DP真的十分优美,前提是要学会了qwq
完结撒花
手写不易qwq