Codeforces Round #530 (Div. 2)F. Cookies(dp+线段树)

题意:给定一颗n(1e5)个点的树,一个时间T(1e18),接下来给你n个数x[i](1e6),表示i节点上有x[i]个饼干,接下来继续给你n个数t[i](1e6)表示i节点上的饼干吃一个要t[i]时间。

现有博弈,你从1节点先手

     1.你可以选择走先一个儿子

     2.后手断掉一个儿子,不让你走。

走完后回到1,回的时候可以吃一些饼干,问最多能吃多少饼干。

 

题解:首先考虑到节点v时,最多能吃多少饼干,由于1到v的t[i[,x[i]都知道,贪心取t[i]最小的就好,这一部分可以用线段树来维护。

           接下来是博弈的部分,用dp来实现,假设dp[u]表示u子树中的一个节点到u的最大答案。

           转移就是:(u!=1)dp[u]=max(dp[u],mx2[v])

                             (u==1)dp[u]=max(dp[u],mx1[v])

 

代码:

#include<bits/stdc++.h>
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define ll long long
using namespace std;
const int N=1e6+9;
int n,x[N],t[N];
ll T;
struct Edge{ll v,w,nxt;}e[N<<2];
int head[N],cnt;
inline void add(int u,int v,ll w){
    e[cnt]=(Edge){v,w,head[u]};head[u]=cnt++;
}
ll sum[N<<2],g[N<<2];
void update(int p,int l,int r,int qx,int qz){
    if(l==r)g[p]+=qz,sum[p]+=1ll*qx*qz;
    else{
        int mid=(l+r)>>1;
        if(qx<=mid)update(ls(p),l,mid,qx,qz);
        else update(rs(p),mid+1,r,qx,qz);
        g[p]=g[ls(p)]+g[rs(p)],sum[p]=sum[ls(p)]+sum[rs(p)];
    }
}
ll query(int p,int l,int r,ll z){
    if(l==r)return min(z/l,g[p]);
    else{
        int mid=(l+r)>>1;
        if(z<=sum[ls(p)])return query(ls(p),l,mid,z);
        else return g[ls(p)]+query(rs(p),mid+1,r,z-sum[ls(p)]);
    }
}
ll dfs(int u,ll T){
 //   cout<<u<<" "<<T<<endl;
    update(1,1,N,t[u],x[u]);
    ll ans=query(1,1,N,T),mx1=0,mx2=0;
    for(int i=head[u];~i;i=e[i].nxt){
        if(T<=2*e[i].w)continue;
         ll t=dfs(e[i].v,T-2*e[i].w);
         if(t>mx1)mx2=mx1,mx1=t;
         else if(t>mx2)mx2=t;
    }
    if(u==1)ans=max(ans,mx1);
    else ans=max(ans,mx2);
   // cout<<ans<<endl;
    update(1,1,N,t[u],-x[u]);
    return ans;
}
int main(){
  //  freopen("tt.in","r",stdin),freopen("tt.out","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>T;
    for(int i=1;i<=n;i++)cin>>x[i];
    for(int i=1;i<=n;i++)cin>>t[i];
    memset(head,-1,sizeof(head));
    for(ll i=2,p,w;i<=n;i++)cin>>p>>w,add(p,i,w);
    ll ans=dfs(1,T);
    cout<<ans<<endl;
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值