题解 P7443 【「EZEC-7」加边】
可以考虑先 DP 一遍求出在不加边的情况下,每个节点是必胜态还是必败态。
首先显然在根节点与 Alice 的先后手操作状态相符合时根本不需要加边。
然后需要证明一个结论:加的这一条边,一定不会是一条回边。
证明也很简单,因为如果是回边的话,那么在这条加入的边起点上,如果是必胜态,那么当前操作者一定不会进入回边,如果是必败态,当前操作者一定会选择走向回边,然后构成循环。
因此,加入回边,要么不会影响结果,要么会使得操作过程陷入死循环。
如何修改一个点的状态?
考虑加边的影响,加这条边只可能将一个必败点转变为必胜点。
将一个必败节点转为必胜态,只需要其中一个节点转变为必败态,或者加一条边连接一个合法的必败态节点即可。
将一个必胜节点转为必败节点。一个必胜节点能被改为必败节点,当且仅当其子节点只有一个必败节点,修改的代价为将这个必败子节点转变为必胜点的代价。
那么,在遍历的时候,可以对于必胜节点,处理一下这个节点是否只有一个必败子节点以及这个子节点的编号。
对于必败节点,则只需对所有子节点代价取最小值,然后再与直接加边取个最小值即可。
如何求直接加边的代价
发现题目中的式子,对于固定的起点,
这个可以用线段树实现。每次将在搜索栈中的节点标记为不可抵达(即将线段树对应位置上的权值标记为无穷大)访问完毕时再单点修改回来即可。查询时访问全局最小值。
线段树常数太大?被卡成 90pts。因为只需要单点修改,查询全局最值,那就用 zkw 树。复杂度带
Ex:这个步骤一种线性实现的方式
遍历时对每个节点处理出以该节点为根的最小值,以及每个点所有出点按遍历顺序的前后缀最小值。
第二次遍历的时候进入下一层子节点的时将 当前节点的答案 与即将进入节点遍历顺序
对当前节点查询的时候取的点为 当前节点的答案 与 所有子节点的子树的最小值 的最小值。
这段话说的好乱啊,希望断句有点帮助吧。
代码:
只有用了 zkw 树的。
#include <bits/stdc++.h>
#define LL __int128
using namespace std;
template <typename Temp> inline void read(Temp & res) {
Temp fh = 1; res = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') fh = -1;
for(; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ '0');
res = res * fh;
}
template <typename Temp> inline void Checkmin(Temp & num, Temp comp) {
if(comp < num) num = comp;
}
const int Maxn = 2e5 + 5;
const LL INF = 0x7fffffffffffffff / 2;
struct e {
int to, nxt;
} b[Maxn];
int head[Maxn], ecnt;
void add(int u, int v) {b[++ecnt] = (e) {v, head[u]}; head[u] = ecnt;}
bool g[Maxn], h[Maxn]; LL f[Maxn];
int n, w, x, L;
LL A, B, a[Maxn];
#define ls t << 1
#define rs t << 1 | 1
LL data[Maxn << 2];
void modify(int pos, LL d) {
pos += L; data[pos] = d;
while(pos >>= 1) data[pos] = min(data[pos << 1], data[pos << 1 | 1]);
}
#undef ls
#undef rs
void dfs1(int t) {
int cnt = 0;
for(int i = head[t]; i; i = b[i].nxt) {
dfs1(b[i].to);
if(!g[b[i].to]) cnt++;
}
if(cnt == 0) g[t] = 0, h[t] = 0;
else g[t] = 1, h[t] = (cnt == 1);
return;
}
void dfs2(int t) {
if(g[t] == 0) modify(t, INF);
for(int i = head[t]; i; i = b[i].nxt) {
dfs2(b[i].to);
if(!g[t]) Checkmin(f[t], f[b[i].to]);
else if(h[t]) {
if(!g[b[i].to]) f[t] = f[b[i].to];
}
}
if(!g[t]) Checkmin(f[t], A * a[t] + B * data[1]);
if(g[t] == 0) modify(t, a[t]);
}
inline void Main() {
read(n); read(w); read(A); read(B); ecnt = 0; L = 1; while(L <= n) L <<= 1;
for(int i = 1; i <= n; ++i) head[i] = 0, f[i] = INF;
for(int i = 1; i <= (n << 2); ++i) data[i] = INF;
for(int i = 2; i <= n; ++i) read(x), add(x, i);
for(int i = 1; i <= n; ++i) read(a[i]);
dfs1(1);
if((w == 0 && g[1]) || (w == 1 && (!g[1]))) {printf("0\n"); return;}
for(int i = 1; i <= n; ++i) if(!g[i]) modify(i, a[i]);
dfs2(1);
printf("%lld\n", (f[1] != INF ? f[1] : -1));
return;
}
int main() {
int T; read(T);
while(T--) Main();
return 0;
}