Codeforces1099F. Cookies(线段树+dp+贪心+博弈)

题目链接:传送门

思路:

  分析到处理节点时的吃cookie的顺序了,然鹅不会用线段树维护前缀和。技术门槛QAQ。。。

  很容易想到可以从root开始搜索,每次深入消耗时间2*边权w。

  然后对于深入到点u开始返回的话,想要尽量多地吃cookie,就要贪心地选择用时短的cookie,也就是:

    当前节点为u,剩余时间为val时,最多能在1-u这条链上吃到多少个cookie。

  一共有1e6个节点,所以这个贪心策略的实现复杂度要压到log级别。。。好难不会。


思路参考:Dream_maker_yk的博客

  线段树维护前缀和。

  首先我们以时间ti为坐标向线段树中插入节点。保存两个值,把子树吃光所用的总时间sum,子树中的cookie总数cnt。

  然后根据剩余时间val查询,如果左子树的sum比val小,那么说明左子树可以吃光,那么查询结果就是:

    cnt左子树 + 对右子树查询val - sum左子树

  这样我们就可以用logn的时间实现贪心策略了。


  然后考虑到Vasya会干扰我们,所以应题目要求,我们要求在Vasya干扰得最好的情况下,能吃到的最大的cookie数量。

  最差的情况就是Vasya每次深入后都把最好的子树f1给办掉了,那么我们只能选次好的子树f2。

  因为每个节点都可能返回,所以回溯当前节点的最多cookie数ans。而当前节点的最多cookie数可能是吃到当前节点为止,也可能是吃到当前节点的子树,所以ans = max(ans,f2)。

  特别地,因为Mitya是先手,所以root的子树不会被办,ans = max(ans,f1)。

代码:

#include <bits/stdc++.h>
#define lson (pos << 1)
#define rson (pos << 1 | 1)

using namespace std;
typedef long long ll;
const int MAX_N = 1e6 + 5;

struct Edge{
    int v;
    ll w;
};
ll x[MAX_N], t[MAX_N];
vector <Edge> g[MAX_N];

ll sum[MAX_N<<2], cnt[MAX_N<<2];

void update(int pos, int l, int r, ll xi, ll ti) {
    sum[pos] += xi*ti;
    cnt[pos] += xi;
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    if (ti <= mid)
        update(lson, l, mid, xi, ti);
    else
        update(rson, mid+1, r, xi, ti);
}

ll query(int pos, int l, int r, ll val) {
    if (l == r)
        return min(cnt[pos], val/l);
    int mid = (l + r) >> 1;
    if (sum[lson] <= val)
        return cnt[lson] + query(rson, mid+1, r, val - sum[lson]);
    else
        return query(lson, l, mid, val);
}

ll dfs(int u, ll res) {
    update(1, 1, 1e6, x[u], t[u]);
    ll ans = query(1, 1, 1e6, res);
    ll f1 = 0, f2 = 0;
    for (const auto &tmp : g[u]) {
        int v = tmp.v;
        ll w = tmp.w;
        if (res < 2*w)
            continue;
        ll f = dfs(v, res - 2*w);
        if (f > f1)
            f2 = f1, f1 = f;
        else if (f > f2)
            f2 = f;
    }
    update(1, 1, 1e6, -x[u], t[u]);
    if (u == 1)
        return max(ans, f1);
    else
        return max(ans, f2);
}

int main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    ll T;
    cin >> n >> T;
    for (int i = 1; i <= n; i++)
        cin >> x[i];
    for (int i = 1; i <= n; i++)
        cin >> t[i];
    for (int u = 2; u <= n; u++) {
        int v;
        ll w;
        cin >> v >> w;
        g[v].push_back((Edge){u, w});
    }

    ll ans = dfs(1, T);

    cout << ans << endl;
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Lubixiaosi-Zhaocao/p/10242048.html