Luogu 5439 XR-2永恒
是节点数为 的那棵树, 是 Trie 树。带 的,比如 ,表示 Trie 上的信息(注意到 要从 开始),不带的表示原树。 表示 的路径, 是原树上无序点对的全集。
那么答案就是:
考虑枚举 分情况计算贡献:
- :显然包含 的路径数为
- 否则设 为 的祖先, 为 向 延伸的那个儿子,那么经过 的路径数为 。
考虑第一种贡献咋算,根据某道经典题的套路,一个简单的想法是枚举 ,然后 上面所有点增加 ,然后枚举 ,对 求和乘上 即可。但是要除掉根的贡献,因为 。
然后对于第二种贡献,我们直接对 搜一边,到了一个点 ,考虑它到 的路径对 的贡献和。我们到一个点 ,枚举出边 ,直接给 加上 ,然后往 走,回溯的时候再减去即可。
但是这样会算重,发现在第一种贡献中,在 子树内的 也被计算了。冷静分析一下,因为算重的 均满足 在 子树内,所以我们做第二种贡献的时候将增量 变成 即可。
然后线段树随便做了,复杂度 。自认为写得很清新。
#include <bits/stdc++.h>
using namespace std;
namespace vbzIO {
char ibuf[(1 << 20) + 1], *iS, *iT;
#if ONLINE_JUDGE
#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
#else
#define gh() getchar()
#endif
#define pc putchar
#define pi pair<int, int>
#define tu3 tuple<int, int, int>
#define tu4 tuple<int, int, int, int>
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second
#define pb push_back
#define ins insert
#define era erase
inline int read () {
char ch = gh();
int x = 0;
bool t = 0;
while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
return t ? ~(x - 1) : x;
}
inline void write(int x) {
if (x < 0) {
x = ~(x - 1);
putchar('-');
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
}
using vbzIO::read;
using vbzIO::write;
const int mod = 998244353;
const int maxn = 3e5 + 300;
const int inv2 = (mod + 1) / 2;
struct seg { int sum, tag; } tr[maxn << 2];
int n, m, rt, dfc, ans, fa[maxn], a[maxn];
int sz[maxn], dep[maxn], siz[maxn], fath[maxn], son[maxn], top[maxn], id[maxn], d[maxn];
vector<int> t1[maxn], t2[maxn];
#define ls x << 1
#define rs x << 1 | 1
#define mid ((l + r) >> 1)
void pushup(int x) { tr[x].sum = (tr[ls].sum + tr[rs].sum) % mod; }
void pushtag(int x, int c, int l, int r) { (tr[x].sum += 1ll * c * (r - l + 1) % mod) %= mod, (tr[x].tag += c) %= mod; }
void pushdown(int x, int l, int r) {
if (!tr[x].tag) return;
pushtag(ls, tr[x].tag, l, mid), pushtag(rs, tr[x].tag, mid + 1, r);
tr[x].tag = 0;
}
void upd(int l, int r, int s, int t, int c, int x) {
if (s <= l && r <= t) return pushtag(x, c, l, r);
pushdown(x, l, r);
if (s <= mid) upd(l, mid, s, t, c, ls);
if (t > mid) upd(mid + 1, r, s, t, c, rs);
pushup(x);
}
int qry(int l, int r, int s, int t, int x) {
if (s <= l && r <= t) return tr[x].sum;
int res = 0;
pushdown(x, l, r);
if (s <= mid) (res += qry(l, mid, s, t, ls)) %= mod;
if (t > mid) (res += qry(mid + 1, r, s, t, rs)) %= mod;
return res;
}
void updp(int u, int c) {
c %= mod;
while (u) {
upd(0, m, id[top[u]], id[u], c, 1);
u = fath[top[u]];
}
}
int qryp(int u) {
int res = 0;
while (u) {
(res += qry(0, m, id[top[u]], id[u], 1)) %= mod;
u = fath[top[u]];
}
return res;
}
void dfs1(int u, int fat) {
siz[u] = 1, dep[u] = dep[fat] + 1;
for (int v : t2[u]) {
dfs1(v, u), siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int pre) {
top[u] = pre, id[u] = ++dfc;
if (son[u]) dfs2(son[u], pre);
for (int v : t2[u]) {
if (v == son[u]) continue;
dfs2(v, v);
}
}
void dfs3(int u) {
sz[u] = 1;
for (int v : t1[u])
dfs3(v), sz[u] += sz[v];
}
void dfs4(int u) {
for (int v : t2[u])
dfs4(v), (d[u] += d[v]) %= mod;
}
void dfs5(int u, int fat) {
(d[u] += d[fat]) %= mod;
for (int v : t2[u]) dfs5(v, u);
}
void dfs6(int u) {
(ans += 1ll * sz[u] * qryp(a[u]) % mod) %= mod;
for (int v : t1[u]) {
updp(a[u], mod + n - sz[u] - sz[v]);
dfs6(v);
updp(a[u], mod - n + sz[u] + sz[v]);
}
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) {
fa[i] = read();
if (!fa[i]) rt = i;
else t1[fa[i]].pb(i);
}
for (int i = 1; i <= m; i++) {
fath[i] = read();
if (fath[i]) t2[fath[i]].pb(i);
}
scanf("%*s");
for (int i = 1; i <= n; i++) a[i] = read();
for (int u : t2[1]) dep[u] = 1, fath[u] = 0, dfs1(u, 0), dfs2(u, u);
dfs3(rt);
for (int i = 1; i <= n; i++) (d[a[i]] += sz[i]) %= mod;
dfs4(1), d[1] = 0, dfs5(1, 0);
for (int i = 1; i <= n; i++) {
ans = (ans + 1ll * sz[i] * d[a[i]] % mod) % mod;
ans = (ans + mod - 1ll * sz[i] * sz[i] % mod * dep[a[i]] % mod) % mod;
}
ans = 1ll * ans * inv2 % mod;
dfs6(rt);
write(ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!