[The 2020 ICPC Asia Macau Regional Contest] I. Nim Cheater

题目大意

开始是一个空的多重集,然后两种操作:

  1. ADD a b: 加入一个数值为a,权值为b的元素。
  2. DEL: 删除最近一次ADD操作

然后,每次操作过后输出要让这些a异或起来为0,要删除哪些a,且要使得b的和最小。

题解

如果没有题目的8M空间限制,那么我们有dp方程

\[f[i][j] = min\{f[i-1][j], f[i-1][j\oplus a[i]]+b[i]\} \]

其中,\(f[i][j]\)表示前i个数取一些出来异或和为j的最小值。如果遇到DEL操作,我们就回滚版本就行了,就是把i往上退一格。

但是,有空间的限制显然是不行的,8M大约只能开2000000个int。

因为删除就是一个回溯过程,类似dfs,考虑建树。当加入一个数,就新建一个节点,然后删除就相当于回溯,这样一棵树就出来了。然后我们每次最多就会保存一条链。但是我们会发现依然没有优化多少,因为一条链的情况就会卡掉。再考虑轻重树链剖分,一条链上最多有\(\log n\)条轻边。然后我们想,如果是轻边,我就复制一份dp数组,如果是重边,直接滚动数组或者说覆盖。这也就是说一条链上最多会有\(\log n * 16384\)个int,非常满足题目条件。

代码

//#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define PB push_back
#define SZ(x) (int)x.size()
#define MK make_pair
#define ALL(x) x.begin(), x.end()
#define lc (o<<1)
#define rc ((o<<1)|1)

void debug() {
    cerr << endl;
}

template<typename Head, typename... Tail>
void debug(Head H, Tail... T) {
    cerr << " " << H;
    debug(T...);
}

#define dbg(...) cerr << "[" << #__VA_ARGS__ << "]:", debug(__VA_ARGS__)
#define endl '\n'
using LL = long long;

const int _ = 5, maxn = 40000;

int son[maxn+_], now, a[maxn+_], b[maxn+_], fa[maxn+_], n, tot, w[maxn+_];
int first[maxn+_], tote = 0, ans[maxn+_], head[maxn+_];
pair<int, int> id[maxn+_];
int dp[800005];

struct Edge {
    int y, next;
    Edge(int y=0, int next=-1): y(y), next(next) {}
}edges[maxn+_];

void addedge(int x, int y) {
    edges[tote] = Edge(y, first[x]);
    first[x] = tote++;
}

void dfs(int x) {
    son[x] = 1;
    int maxt = 0;
    w[x] = -1;
    for (int i = first[x]; i != -1; i = edges[i].next) {
        dfs(edges[i].y);
        son[x] += son[edges[i].y];
        if (maxt < son[edges[i].y]) maxt = son[edges[i].y], w[x] = edges[i].y;
    }
}

const int M = 1<<14;

void dfsdp(int x, int last, int st, int xorsum) {
    if (x) {
        if (last == st) {
            for (int i = 0; i < M; ++i)
                dp[st+M+i] = min(dp[st+i], dp[st+(i^a[x])] + b[x]);
            for (int i = 0; i < M; ++i)
                dp[st+i] = dp[st+M+i];
        }
        else {
            for (int i = 0; i < M; ++i)
                dp[st+i] = min(dp[last+i], dp[last+(i^a[x])] + b[x]);
        }
    }
    for (int i = head[x]; i != -1; i = id[i].Y)
        ans[id[i].X] = dp[st+xorsum];
    for (int i = first[x]; i != -1; i = edges[i].next)
        if (edges[i].y != w[x]) dfsdp(edges[i].y, st, st + M, xorsum^a[edges[i].y]);
    if (w[x] > -1) dfsdp(w[x], st, st, xorsum^a[w[x]]);
}

int main() {
    int T = 1;
    for (int kase = 1; kase <= T; ++kase) {
        scanf("%d", &n);
        tot = 0;
        memset(fa, 0, sizeof(fa));
        memset(first, -1, sizeof(first));
        memset(head, -1, sizeof(head));
        memset(id, -1, sizeof(id));
        int t = 0;
        now = 0;
        for (int i = 0; i < n; ++i) {
            char cmd[5];
            scanf("%s", cmd);
            if (cmd[0] == 'A') {
                fa[++tot] = now;
                addedge(now, tot);
                now = tot;
                scanf("%d%d", &a[now], &b[now]);
            }
            else now = fa[now];
            id[t] = MK(i, head[now]);
            head[now] = t++;
        }
        dfs(0);
        memset(dp, 0x3f, sizeof(dp));
        dp[0] = 0;
        dfsdp(0, 0, 0, 0);
        for (int i = 0; i < n; ++i)
            printf("%d\n", ans[i]);
    }
    return 0;
}
posted @ 2021-10-28 17:18  albertxwz  阅读(192)  评论(0编辑  收藏  举报