Interstellar 题解

一、题目:



二、思路:

在没看数据范围之前,这道题有一个十分显然的树形DP做法。

\(dp(x)\) 表示 \(x\) 及其子树内所能获得的最大代价(题目为什么要使代价最大化呢?),于是有转移方程

\[dp(x)=\max_{y\in son(x)}\{dp(y)+w(x,y)\}+\sum_{y\in son(x)} (dp(y)+w(x,y)) \]

时间复杂度 \(O(n)\),这题不就解决了吗?

然后看到数据范围 \(0\leq c_i\leq 10^{18}\),也就是说每条边的权值最大可以是 \(2^{10^{18}}\),当场去世。

于是考虑用 set 维护每条边的权值中有哪几个2的幂次。加法和比较大小都类似于高精度。

考虑这样的复杂度为什么是对的,因为每条边至多只会贡献一个2的幂次,而且加法每进一位都会少一个1,所以每次均摊复杂度是 \(O(1)\) 的。如果我们再用启发式合并,那总的复杂度就是 \(O(n\log^2 n)\)的。比较大小也一样。

再考虑我们还要取出最大的数进行累加,这时候相当于对一个数乘2,也就是每一个2的幂次都会左移。暴力移肯定不行,考虑使用懒标记。

然后呢?然后就没了。

三、启示:

其实这样的做法很容易想到,这启发我们需要认真分析复杂度,而且当不方便实时维护的时候一定要想到懒标记,有时候能解决很多问题。

四、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>

using namespace std;

#define FILEIN(s) freopen(s".in", "r", stdin)
#define FILEOUT(s) freopen(s".out", "w", stdout)

inline long long read(void) {
    long long x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 1e5 + 5, mod = 998244353;

int n, tot = 1, head[maxn];
long long ans[maxn];

struct Edge {
    int y, next;
    long long w;
    Edge() {}
    Edge(int _y, int _next, long long _w) : y(_y), next(_next), w(_w) {}
}e[maxn << 1];

struct Node {
    set<long long>S;
    long long tag;
    inline void insert(long long p) {
        p -= tag;
        while (S.find(p) != S.end()) {
            S.erase(p);
            ++ p;
        }
        S.insert(p);
    }
    inline friend bool operator <(const Node&a, const Node&b) {
        if (b.S.size() == 0) return 0;
        if (a.S.size() == 0) return 1;
        set<long long>::iterator i1 = a.S.end(), i2 = b.S.end();
        -- i1; -- i2;
        while ((*i1) + a.tag == (*i2) + b.tag) {
            if (i2 == b.S.begin()) return 0;
            if (i1 == a.S.begin()) return 1;
            -- i1; -- i2;
        }
        return (*i1) + a.tag < (*i2) + b.tag;
    }
}dp[maxn];

inline long long power(long long a, long long b) {
    long long res = 1;
    for (; b; b >>= 1) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
    }
    return res;
}

inline void connect(int x, int y, long long w) {
    e[++ tot] = Edge(y, head[x], w);
    head[x] = tot;
}

void dfs(int x, int fa) {
    int mx = -1;

    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        if (y == fa) continue;
        dfs(y, x);
        ans[y] += power(2, e[i].w);
        dp[y].insert(e[i].w);
        if (mx == -1 || dp[mx] < dp[y]) mx = y;
    }

    if (mx != -1) {
        ++ dp[mx].tag;
        (ans[mx] *= 2) %= mod;
    }

    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        if (y == fa) continue;

        if (dp[y].S.size() > dp[x].S.size()) swap(dp[y], dp[x]);

        for (set<long long>::iterator it = dp[y].S.begin(); it != dp[y].S.end(); ++ it) {
            dp[x].insert(*it + dp[y].tag);
        }

        (ans[x] += ans[y]) %= mod;
    }

}

int main() {
    FILEIN("a"); FILEOUT("a");
    n = read();
    for (int i = 1; i < n; ++ i) {
        int p = read(); long long w = read();
        connect(i + 1, p, w);
        connect(p, i + 1, w);
    }
    dfs(1, 0);
    printf("%lld\n", ans[1]);
    return 0;
}
posted @ 2021-05-12 21:22  蓝田日暖玉生烟  阅读(42)  评论(0编辑  收藏  举报