Codeforces Round #530 (Div. 2)F Cookies (树形dp+线段树)
题:https://codeforces.com/contest/1099/problem/F
题意:给定一个树,每个节点有俩个信息x和t,分别表示这个节点上的饼干个数和先手吃掉这个节点上一个饼干的的时间。然后有先手和后手俩个人。
◉先手可以这么操作:在规定总时间T到达某个节点然后一定要返回根节点1,期间可以选择吃掉某些节点上的某些饼干(前提是保证剩下的时间能够回到根节点);
◉后手可以这么操作:在先手到达的位置和这个位置的孩子之间的连边选择一条让先手吃得更多的边摧毁掉,也可以跳过这个过程;
问:在给定的总时间T内,先手最多能吃到多少的饼干。
分析:对于每个节点都有3个选择,我们记为dp1,dp2,dp3;
dp1:从当前位置上去能吃到的最多的饼干;
dp2:从当前位置沿孩子下去能吃到的最多的饼干数;(这一步只在当前位置为根节点的时候起作用,因为是先手,所以可以直接选最大)
dp3:从当前位置沿孩子下去能吃到的次多的饼干数;(因为后手会阻止先手吃最大的饼干数,所以就选次大)
dfs处理这棵树,回溯的时候做dp2,dp3的计算即可;
然后饼干数的最大和次大我们把节点信息的x和t,全部转化为时间消耗,x*t对应吃了x个饼干,然后二分出答案即可。
#include<bits/stdc++.h> using namespace std; #define pb push_back #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r typedef long long ll; const int M=1e6+5; ll x[M],t[M]; struct node{ int u; ll w; }; vector<node>g[M]; struct Node{ ll ti,num; }tree[M<<2]; int n; void update(ll num,ll ti,ll root,ll l,ll r){ if(l==r){ tree[root].ti+=num*ti; tree[root].num+=num; return ; } int midd=(l+r)>>1; if(ti<=midd) update(num,ti,lson); else update(num,ti,rson); tree[root].ti=tree[root<<1].ti+tree[root<<1|1].ti; tree[root].num=tree[root<<1].num+tree[root<<1|1].num; } ll query(ll nowti,ll root,ll l,ll r){ if(tree[root].ti<=nowti) return tree[root].num; if(l==r) return nowti/l; int midd=(l+r)>>1; if(tree[root<<1].ti>=nowti) return query(nowti,lson); else return query(nowti-tree[root<<1].ti,rson)+tree[root<<1].num; } ll dfs(int u,ll nowt){ //cout<<u<<endl; update(x[u],t[u],1,1,1000000); ll dp1=query(nowt,1,1,1000000); ll dp2=0,dp3=0; for(int i=0;i<g[u].size();i++){ node v=g[u][i]; if(nowt<=2*v.w) continue; ll temp=dfs(v.u,nowt-2ll*v.w); if(temp>dp2) dp3=dp2,dp2=temp; else if(dp3<temp) dp3=temp; } update(-x[u],t[u],1,1,1000000); if(u==1)///根节点,先走,所以不用考虑次大 return max(dp1,dp2); else return max(dp1,dp3); } int main(){ ll T; scanf("%d%I64d",&n,&T); for(int i=1;i<=n;i++) scanf("%I64d",&x[i]); for(int i=1;i<=n;i++) scanf("%I64d",&t[i]); for(int i=2;i<=n;i++){ ll w; int u; scanf("%d%I64d",&u,&w); g[u].pb(node{i,w}); } printf("%I64d",dfs(1,T)); return 0; }