填树
题意:
给出 个点的树,初始时每个点的权值为 。每次可以选择树上一条路径,可以将路径上的每个点 赋上 的权值,并且满足这条路径上最大最小值之差 。
求这样做后,本质不同的树的个数 以及 本质不同的树的权值之和。
路径互不相同
- 容易发现选出的不同的路径赋值后所构成的树一定不相同。因此,每一条路径就可以看成一个序列,路径条数是 的, 这样就转化成序列问题:每次给定 个点,每个点有限制,满足最大最小值之差 。
容斥
-
考虑枚举最小值 , 对于每个点 , 容易发现最小的能取的数是 , 最大能取的数是 。
-
当最大的数 最小的数,就取不了数。
那么想到每个点选择相互独立,就有下面的式子:
是值域。
实际上,这里算出来的方案数是有可能是选不到 的,可以理解为求了最小值 的方案数,要强制令其选到至少一个 ,也就是要求 最小值 的方案数。
不妨设 表示对每个点再限制一个范围 的本质不同的序列个数,上面实际上求的是 。
那我们实际要求的是 。
- 直观理解为 。
这样的时间复杂度是 。
拉格朗日插值优化
目前首要的任务是优化掉 。
- 考虑如何计算 ,
这个 不好搞,考虑分类讨论,下面会讨论 中每一个点的取值。
-
考虑什么时候会取到 :
发现只要 和 没有交集,自然会取 ,
有 和 。
-
对 和 考虑。
经过大力讨论,每个点会得到一个分段函数,
总的来说, 令 。
这里满足 , 如果 , 那么 互换。
同理要减去的那部分的式子也差不多。
这里给出分段函数的式子:
令 。
- 不妨重新令 , 。
- 方案数为:
- 每个 是个一次函数,这个 就是 次的多项式。
在令 做前缀和,它就是 次的多项式。
只要用连续 个点就能用 lagrange插值 计算出一段的和,
由于每个 有 段,对于所有点的所有分段点重排后,在每对相邻点计算区间的和即可。
时间复杂度 。
- 对于第二问树的带权和,
考虑一个点的贡献,计算剩下点能构成的合法路径条数,乘上每个点能选出的值的和。
对每个点求和即是答案,计算方式也和上面差不多。
分段函数改成等差数列求和即可。
树形dp
现在瓶颈就在枚举出所有路径,
必须得将路径共同考虑。
先考虑用 限制整个树的取值,要求不同的树的个数 , 权值和 。
-
用 表示所有子树中的叶子到 的路径不同赋值方案的和,
表示所有子树中的叶子到 的路径不同赋值方案的点权和。 -
对于每个点初始状态,
, 表示根到根的路径的不同方案。
, 表示点权和。
分别加入 , 。
-
对最浅点是该点的路径计算,考虑像背包合并子树一样的过程,枚举子树 。
-
每次都能和之前的路径连接上:对 贡献 。
-
之前的路径还能接上新的路径,两条路径贡献分别增多:对于 贡献 。
-
对于 : 选 点形成新路径: 。
-
对于 : 多出的贡献以及 点多出的贡献: 。
-
-
这时还是枚举最小值 , 用 表示限制 下,最终答案。
容斥: 。
发现 ,这个 是前前面推的分段函数。
仍然考虑拉格朗日插值,按照转折点分段。
这时时间复杂度 。
int n, m, K;
int L[MAXN], R[MAXN], l[MAXN], r[MAXN], d[MAXN];
vector<int> e[MAXN];
int inv[MAXN];
int lagrange(vector<pair<int, int>> &v, int x) {
int sum = 0, n = v.size();
for (auto i : v)
if (i.first == x)
return i.second;
int prod1 = 1, p = n & 1 ? 1 : mod - 1;
for (int i = 0; i < n; i ++)
prod1 = mul(prod1, dec(x, v[i].first));
for (int i = 0; i < n; i ++) {
sum = add(sum, mul(v[i].second, mul(mul(prod1, qpow(dec(x, v[i].first), mod - 2)), mul(mul(inv[i], inv[n - i - 1]), p))));
p = mod - p;
}
return sum;
}
int f[MAXN], g[MAXN];
int ans1, ans2, val1[MAXN], val2[MAXN];
void dfs(int u, int fa) {
ans1 = add(ans1, (f[u] = val1[u]));
ans2 = add(ans2, (g[u] = val2[u]));
for (int v : e[u]) {
if (v == fa) continue;
dfs(v, u);
ans1 = add(ans1, mul(f[u], f[v]));
ans2 = add(ans2, add(mul(g[u], f[v]), mul(g[v], f[u])));
f[u] = add(f[u], mul(val1[u], f[v]));
g[u] = add(g[u], add(mul(val1[u], g[v]), mul(val2[u], f[v])));
}
}
inline int G(int l, int r) {
if (l > r) return 0;
return 1ll * (l + r) * (r - l + 1) / 2 % mod;
}
pair<int, int> calc(int l, int r) {
ans1 = ans2 = 0;
for (int i = 1; i <= n; i ++) {
if (R[i] < l || r < L[i]) val1[i] = val2[i] = 0;
else {
val1[i] = min(r, R[i]) - max(l, L[i]) + 1;
val2[i] = G(max(l, L[i]), min(r, R[i]));
}
}
dfs(1, 0);
return make_pair(ans1, ans2);
}
pair<int, int> calc(int x) {
auto a = calc(x, x + K), b = calc(x + 1, x + K);
return make_pair(dec(a.first, b.first), dec(a.second, b.second));
}
pair<int, int> solve(int l, int r) {
vector<pair<int, int>> v1, v2;
int sum1 = 0, sum2 = 0;
for (int i = l; i <= min(r, l + n + 2); i ++) {
auto p = calc(i);
sum1 = add(sum1, p.first);
sum2 = add(sum2, p.second);
v1.emplace_back(i, sum1);
v2.emplace_back(i, sum2);
}
return make_pair(lagrange(v1, r), lagrange(v2, r));
}
int main() {
cin >> n >> K;
int p = 1;
for (int i = 1; i <= n + 3; i ++) p = mul(p, i);
inv[n + 3] = qpow(p, mod - 2);
for (int i = n + 2; i >= 0; i --)
inv[i] = mul(inv[i + 1], i + 1);
int mi = INF, ma = 0;
for (int i = 1; i <= n; i ++) {
cin >> L[i] >> R[i];
mi = min(mi, L[i]);
ma = max(ma, R[i]);
d[++ m] = L[i];
d[++ m] = max(L[i] - 1, 0);
d[++ m] = max(L[i] - K, 0);
d[++ m] = max(R[i] - K, 0);
d[++ m] = R[i];
}
sort(d + 1, d + m + 1);
m = unique(d + 1, d + m + 1) - d - 1;
for (int i = 1; i < n; i ++) {
int u, v;
cin >> u >> v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
int ans1 = 0, ans2 = 0;
for (int i = 2; i <= m; i ++) {
auto ans = solve(d[i - 1], d[i] - 1);
ans1 = add(ans1, ans.first);
ans2 = add(ans2, ans.second);
}
auto ans = solve(d[m], d[m]);
ans1 = add(ans1, ans.first);
ans2 = add(ans2, ans.second);
cout << ans1 << endl << ans2 << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?