Codeforces Round #530 (Div. 2) E (树形dp+线段树)

链接:

题意:

给你一棵树,树上有n个节点,每个节点上有ai块饼干,在这个节点上的每块饼干需要花费bi时间,有两个玩家,玩家一可以移动到当前点的子节点也可以申请游戏结束返回根节点并吃沿途的饼干,玩家二可以删除当前点到儿子节点的一条边,走路和吃饼干都消耗时间,会给出一个总时间,在总时间内尽可能的多吃饼干,问最多能吃多少个?

思路:

由于是玩家一先手,那么最开始的最大边则不会被删除,但之后路途的最大边都会被玩家二删除,所以我们对于当前点我们需要求:

1.如果现在回头那么最多可以吃到多少饼干

2.向下走最多可以吃到多少饼干

3.向下走次多可以吃到多少饼干

实现代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid ll m = (l + r) >> 1

const ll M = 1e6+10;
const ll Max = 1e6;
struct node{
    ll to,w,next;
}e[M<<1];
ll sum[M<<2],num[M<<2],a[M],tim[M],head[M],cnt;
void add(ll u,ll v,ll w){
    e[++cnt].to = v;e[cnt].next = head[u];e[cnt].w = w;head[u] = cnt;
}

void pushup(ll rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    num[rt] = num[rt<<1] + num[rt<<1|1];
}

void update(ll p,ll c,ll l,ll r,ll rt){
    if(l == r){
        sum[rt] += p*c;
        num[rt] += c;
        return ;
    }
    mid;
    if(p <= m) update(p,c,lson);
    else update(p,c,rson);
    pushup(rt);
}

ll query(ll t,ll l,ll r,ll rt){
    if(sum[rt] <= t) return num[rt];
    if(l == r){
        return t/l;
    }
    mid;
    if(sum[rt<<1] >= t) return query(t,lson);
    else return num[rt<<1] + query(t-sum[rt<<1],rson);
}

ll dfs(ll u,ll fa,ll t){
    update(tim[u],a[u],1,Max,1);
    ll f1 = query(t,1,Max,1);
    ll f2 = 0,f3 = 0;

    for(ll i = head[u];i;i=e[i].next){
        ll v = e[i].to;
        if(v == fa) continue;
        if(t <= e[i].w*2) continue;
        ll k = dfs(v,u,t-e[i].w*2);
        if(k > f2) f3 = f2,f2 = k;
        else if(k > f3) f3 = k;

    }
    update(tim[u],-a[u],1,Max,1);
    if(u == 1) return max(f1,f2);
    else return max(f1,f3);
}

int main()
{
    ll n,T,x,y;
    scanf("%lld%lld",&n,&T);
    for(ll i = 1;i <= n;i ++) scanf("%lld",&a[i]);
    for(ll i = 1;i <= n;i ++) scanf("%lld",&tim[i]);
    for(ll i = 2;i <= n;i ++){
        scanf("%lld%lld",&x,&y);
        add(i,x,y); add(x,i,y);
    }
    printf("%lld\n",dfs(1,0,T));
    return 0;
}

原文地址:https://www.cnblogs.com/kls123/p/10264180.html

时间: 2024-11-10 08:09:35

Codeforces Round #530 (Div. 2) E (树形dp+线段树)的相关文章

Codeforces Round #426 (Div. 2) D. The Bakery(线段树维护dp)

题目链接: Codeforces Round #426 (Div. 2) D. The Bakery 题意: 给你n个数,划分为k段,每段的价值为这一段不同的数的个数,问如何划分,使得价值最大. 题解: 考虑dp[i][j]表示划分为前j个数划分为i段的最大价值,那么这就是一个n*n*k的dp, 考虑转移方程dp[i][j]=max{dp[i][k]+val[k+1][j]},我们用线段树去维护这个max,线段树上每个节点维护的值是dp[i][k]+val[k+1][j],对于每加进来的一个数a

Codeforces Round #424 (Div. 2) E. Cards Sorting(线段树)

题目链接:Codeforces Round #424 (Div. 2) E. Cards Sorting 题意: 将n个数放进一个队列,每次检查队首,看看是不是队列中最小的数,如果是就扔掉,如果不是就放到队尾. 这样直到队列为空,为需要操作多少次. 题解: 考虑用两个指针模拟,最开始now指针指向第一个数,然后nxt指针指向下一个将要被删除的数. 然后我们要算出这里需要移动多少步,然后删掉这个数,一直重复操作,直到将全部的数删完. nxt指针可以用set来维护,now指针可以用并查集来维护. 计

Codeforces 671D. Roads in Yusland(树形DP+线段树)

调了半天居然还能是线段树写错了,药丸 这题大概是类似一个树形DP的东西.设$dp[i]$为修完i这棵子树的最小代价,假设当前点为$x$,但是转移的时候我们不知道子节点到底有没有一条越过$x$的路.如果我们枚举每条路去转移,会发现这条路沿线上的其他子树的答案难以统计,那怎么办呢,我们可以让这条路向上回溯的时候顺便记录一下,于是有$val[i]$表示必修i这条路,并且修完当前子树的最小代价. 则有转移$dp[x]=min(val[j])$,且$j$这条路必须覆盖$x$. $val[i]=(\sum

Codeforces Round #603 (Div. 2) E. Editor(线段树)

链接: https://codeforces.com/contest/1263/problem/E 题意: The development of a text editor is a hard problem. You need to implement an extra module for brackets coloring in text. Your editor consists of a line with infinite length and cursor, which point

Codeforces Round #244 (Div. 2) B. Prison Transfer 线段树rmq

B. Prison Transfer Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/problemset/problem/427/B Description The prison of your city has n prisoners. As the prison can't accommodate all of them, the city mayor has decided to transfer c

Codeforces Round #168 (Div. 1)B 树形dp

//给一棵树,每次操作可以将包括顶点1的连通子集的所有点的节点加1或减1 //问最少几次操作使得这棵树的所有顶点的值都为0 //以1为根节点建树 //将加和减分开考虑,用up[u],down[u]表示以u为跟节点的子树中需要加的操作 //最大为up[u] ,需要减的操作最大为down[u] //其余的加和减的操作则可以在处理这两个操作时一起覆盖 //在将u的子数全都处理完后u点的值由于在加了up[u]和减了down[u]后变为 //一个新的值,则对于这个新的值根据它的正负并入up[u]或down

Codeforces Round #135 (Div. 2) D 树形dp

//任选一个根节点,用dfs搜两遍,第一遍找以该节点为根节点 //每一个节点的子树中的相反的边 //第二遍dfs以同样的根节点搜索一遍,记录从根节点到该节点 //正向边和反向边dp[u] = dp[root] - sum_0 + sum_1 : #include<cstdio> #include<cstring> #include<iostream> using namespace std ; const int maxn = 200010 ; const int in

Codeforces Round #148 (Div. 1)C 树形dp

//枚举所有边,把该树分为两个树,分别求两颗数的最小的改变量 #include<cstdio> #include<cstring> #include<iostream> using namespace std ; const int maxn = 3030 ; const int inf = 0x7fffffff ; int dp[maxn] ; struct Edge { int flag , v; int u ; int next ; }edge[2*maxn] ;

Codeforces Round #587 (Div. 3) F Wi-Fi(线段树+dp)

题意:给定一个字符串s 现在让你用最小的花费 覆盖所有区间 思路:dp[i]表示前i个全覆盖以后的花费 如果是0 我们只能直接加上当前位置的权值 否则 我们可以区间询问一下最小值 然后更新 #include <bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const double eps = 1e-6; const int N = 2e5+7; typedef long long ll; const ll mod