题解:P11097 [ROI 2022] 采购优化 (Day 1)
感觉按照我这个思路看下来,这道题最多只有蓝。
数据结构题。
肯定地,为了满足题目的购买个数的条件,又想让总成本最小,肯定是每棵子树都刚好满足题目条件。
那么我们考虑对于每棵子树中,直接处理。考虑在先前的基础上,若还需要购买,那就找最小的
这个东西树链剖分加线段树和可并堆来维护就行了。
可并堆用的是左偏树来写的。
因为在可并堆里的数只会加入一次和删去一次,所以时间复杂度是可以得到保证的。
粗略分析下来,每次 solve 的时间复杂度是
#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace _ {
const int N = 2e5+10;
const int inf = 0x3f3f3f3f3f3f3f3f;
struct SegTree {
int mi[4*N], tag[4*N];
void doBuild(int k, int l, int r) {
tag[k] = mi[k] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
doBuild(k*2, l, mid);
doBuild(k*2+1, mid+1, r);
}
void doBuild(int k, int l, int r, int a[], int p[]) {
tag[k] = 0;
if (l == r) { mi[k] = a[p[l]]; return; }
int mid = (l + r) >> 1;
doBuild(k*2, l, mid, a, p);
doBuild(k*2+1, mid+1, r, a, p);
mi[k] = min(mi[k*2], mi[k*2+1]);
}
void doChangeOne(int k, int l, int r, int dx) {
mi[k] += dx, tag[k] += dx;
}
void pushDown(int k, int l, int r) {
if (tag[k] == 0) return;
int mid = (l + r) >> 1;
doChangeOne(k*2, l, mid, tag[k]);
doChangeOne(k*2+1, mid+1, r, tag[k]);
tag[k] = 0;
}
void doChange(int k, int l, int r, int x, int y, int dx) {
if (r < x || y < l) return;
if (x <= l && r <= y) { doChangeOne(k, l, r, dx); return; }
pushDown(k, l, r);
int mid = (l + r) >> 1;
doChange(k*2, l, mid, x, y, dx);
doChange(k*2+1, mid+1, r, x, y, dx);
mi[k] = min(mi[k*2], mi[k*2+1]);
}
int doQuery(int k, int l, int r, int x, int y) {
if (r < x || y < l) return inf;
if (x <= l && r <= y) return mi[k];
pushDown(k, l, r); int mid = (l + r) >> 1;
return min(doQuery(k*2, l, mid, x, y), doQuery(k*2+1, mid+1, r, x, y));
}
} Seg;
struct SegTree2 {
int sum[4*N], tag[4*N];
void doBuild(int k, int l, int r) {
tag[k] = sum[k] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
doBuild(k*2, l, mid);
doBuild(k*2+1, mid+1, r);
}
void doChangeOne(int k, int l, int r, int dx) {
sum[k] += (r-l+1)*dx, tag[k] += dx;
}
void pushDown(int k, int l, int r) {
if (tag[k] == 0) return;
int mid = (l + r) >> 1;
doChangeOne(k*2, l, mid, tag[k]);
doChangeOne(k*2+1, mid+1, r, tag[k]);
tag[k] = 0;
}
void doChange(int k, int l, int r, int x, int y, int dx) {
if (r < x || y < l) return;
if (x <= l && r <= y) { doChangeOne(k, l, r, dx); return; }
pushDown(k, l, r);
int mid = (l + r) >> 1;
doChange(k*2, l, mid, x, y, dx);
doChange(k*2+1, mid+1, r, x, y, dx);
sum[k] = sum[k*2] + sum[k*2+1];
}
int doQuery(int k, int l, int r, int x, int y) {
if (r < x || y < l) return 0;
if (x <= l && r <= y) return sum[k];
pushDown(k, l, r); int mid = (l + r) >> 1;
return doQuery(k*2, l, mid, x, y) + doQuery(k*2+1, mid+1, r, x, y);
}
} Seg2;
int n;
int p[N];
vector<int> g[N];
int c[N];
int l[N], r[N];
int b[N];
int siz[N], hson[N], top[N];
int tim, dfn[N], id[N];
bool flag = false;
int hc[N], hid[N];
int lt[N], rt[N], dis[N];
int nowpos;
int root[N];
int makeNode(int cc, int idd) {
int x = ++nowpos;
hc[x] = cc; hid[x] = idd;
lt[x] = rt[x] = 0;
dis[x] = 1;
return x;
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (hc[x] > hc[y] || (hc[x] == hc[y] && hid[x] > hid[y]))
swap(x, y);
rt[x] = merge(rt[x], y);
if (dis[lt[x]] < dis[rt[x]]) swap(lt[x], rt[x]);
dis[x] = dis[rt[x]] + 1;
return x;
}
bool empty(int u) {
return root[u] == 0;
}
int topC(int u) {
return hc[root[u]];
}
int topId(int u) {
return hid[root[u]];
}
void pop(int u) {
root[u] = merge(lt[root[u]], rt[root[u]]);
}
void Union(int u, int v) {
root[u] = merge(root[u], root[v]);
root[v] = 0;
}
void clear() {
Seg.doBuild(1, 1, n);
Seg2.doBuild(1, 1, n);
for (int i = 1; i <= n; i++) {
root[i] = 0;
l[i] = r[i] = c[i] = b[i] = p[i] = 0;
g[i].clear(); siz[i] = hson[i] = top[i] = dfn[i] = id[i] = 0;
}
nowpos = 0;
tim = 0;
}
int query(int u, int fath) {
int res = inf;
while (dfn[top[u]] > dfn[fath]) {
res = min(res, Seg.doQuery(1, 1, n, dfn[top[u]], dfn[u]));
u = p[top[u]];
}
res = min(res, Seg.doQuery(1, 1, n, dfn[fath], dfn[u]));
return res;
}
void dfs(int u, int from) {
siz[u] = 1;
for (int v : g[u]) {
if (v == from) continue;
dfs(v, u); siz[u] += siz[v];
if (siz[v] >= siz[hson[u]]) hson[u] = v;
}
}
void dfs2(int u, int from, int tp) {
id[dfn[u] = ++tim] = u, top[u] = tp;
if (hson[u]) dfs2(hson[u], u, tp);
for (int v : g[u]) {
if (v == from || v == hson[u]) continue;
dfs2(v, u, v);
}
}
void add(int u, int fath, int dx) {
while (dfn[top[u]] > dfn[fath]) {
Seg.doChange(1, 1, n, dfn[top[u]], dfn[u], -dx);
Seg2.doChange(1, 1, n, dfn[top[u]], dfn[u], dx);
u = p[top[u]];
}
Seg.doChange(1, 1, n, dfn[fath], dfn[u], -dx);
Seg2.doChange(1, 1, n, dfn[fath], dfn[u], dx);
}
void calc(int u, int from) {
if (flag) return;
for (int v : g[u]) {
if (v == from) continue;
calc(v, u);
}
if (flag) return;
root[u] = merge(root[u], makeNode(c[u], u));
for (int v : g[u]) {
if (v == from) continue;
int pp = Seg2.doQuery(1, 1, n, dfn[v], dfn[v]);
Seg2.doChange(1, 1, n, dfn[u], dfn[u], pp);
Seg.doChange (1, 1, n, dfn[u], dfn[u], -pp);
Union(u, v);
}
if (Seg2.doQuery(1, 1, n, dfn[u], dfn[u]) > r[u]) {
flag = true;
return;
}
while (Seg2.doQuery(1, 1, n, dfn[u], dfn[u]) < l[u]) {
if (empty(u)) {
flag = true;
return;
}
int cc = topC(u), idd = topId(u);
int most = query(idd, u);
int need = l[u] - Seg2.doQuery(1, 1, n, dfn[u], dfn[u]);
if (most <= need) {
add(idd, u, most);
b[idd] += most;
pop(u);
} else {
add(idd, u, need);
b[idd] += need;
}
}
}
void solve() {
cin >> n;
for (int i = 2; i <= n; i++) {
cin >> p[i];
g[i].push_back(p[i]);
g[p[i]].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n; i++) cin >> l[i] >> r[i];
dfs(1, 0);
dfs2(1, 0, 1);
Seg.doBuild(1, 1, n, r, id);
Seg2.doBuild(1, 1, n);
flag = false;
calc(1, 0);
if (flag) cout << -1 << '\n';
else {
int ans = 0;
for (int i = 1; i <= n; i++) ans += c[i] * b[i];
cout << ans << '\n';
for (int i = 1; i <= n; i++) cout << b[i] << " ";
cout << '\n';
}
}
signed main() {
cin.tie(0)->sync_with_stdio(false);
int T; cin >> T;
while (T--) solve(), clear();
return 0;
}
}
signed main() {
return _::main();
}