[The 2020 ICPC Asia Macau Regional Contest] I. Nim Cheater
阿新 • • 發佈:2021-10-28
題目大意
開始是一個空的多重集,然後兩種操作:
- ADD a b: 加入一個數值為a,權值為b的元素。
- 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\)
程式碼
//#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; }