首页 技术 正文
技术 2022年11月16日
0 收藏 735 点赞 3,295 浏览 3671 个字

显然直接计数是不好计的,只能从 \(dp\) 这个角度来下手。

首先用最原始最直接的方法,直接在 \(dp\) 的过程中满足题目的要求。

既然问题给在一棵树上,那么必然和树脱不了关系,因此我们应该从树形 \(dp\) 的角度下手。

因为在树形 \(dp\) 的过程中我们只能考虑父子边的选择情况,那么对于一个点 \(u\),那些限制链顶在 \(u\) 子树内的限制链显然在其父亲 \(fa\) 进行转移时是考虑不到的,因此设计状态的时候必然要使得这些链已经被满足。

那么再来考虑这些链底在 \(u\) 子树内,链顶在 \(u\) 的祖先上的这些链。

不难发现只需要满足的链是链顶深度最深的这条链,因为只要满足了它其他的链也会被满足。

所以我们在 \(dp\) 的时候只关注于当前伸出去的链中链顶最深的链,于是我们的 \(dp\) 状态就呼之欲出了。

考虑令 \(dp_{i, j}\) 表示以 \(i\) 为根的子树内,还没有被满足限制的链中链顶伸出去的链的最深的链顶深度为 \(j\) 时的方案,特别地 \(j = 0\) 时表示没有伸上来的链。

那么转移的时候只要考虑 \(u \rightarrow v\) 这条父子边选 \(0 / 1\) 即可。

选 \(1\) 时,\(v\) 伸上来的所有链都会被满足,于是有转移:

\[dp_{u, i} \times \sum\limits dp_{v, j} \rightarrow dp_{u, i}
\]

选 \(0\) 时,只需要考虑当前这个最深的链顶是否来自于 \(v\) 即可。

不来自于 \(v\) :

\[dp_{u, i} \times \sum\limits_{j = 0} ^ i dp_{v, j} \rightarrow dp_{u, i}
\]

来自于 \(v\) :

\[dp_{v, i} \sum\limits_{j = 0} ^ i dp_{u, j} \rightarrow dp_{u, i}
\]

需要注意深度相同时 \(dp_{u, i} \times dp_{v, i} \rightarrow dp_{u, i}\) 被计算了两次,需要减去。

但这样依然不能通过本题,怎么办呢?

可以发现,这个 \(dp\) 的状态量就惊人地达到了 \(O(n ^ 2)\),因此我们必须要从状态这里下手。

这时候我们引入一个叫做整体 \(dp\) 的技巧,其适用范围往往是存在一个维度上初始值并不多的情况。

其做法就是将这一个需要优化的维度放到动态开点线段树上,然后可以通过主席树或线段树合并一类技巧来优化这个 \(dp\) 的流程。

那么在本题当中,我们可以将第二维放到动态开点线段树上。

那么对于第一条转移,相当于是给 \(u\) 这个线段树上每个位置乘上 \(v\) 中所有数之和,不难发现这可以直接在线段树上完成。

对于第二条转移,相当于是对于 \(u\) 所在线段树上每个非 \(0\) 的点乘上 \(v\) 所在线段树上 \(u\) 左侧所有位置之和,不难发现这个操作是可以在线段树合并的时候同时完成的,只需要递归时记录当前区间左侧所有数之和即可。

对于第三条转移,本质上与第二条转移没有区别。

因此这个转移的过程就被我们在线段树合并的同时优化掉了,复杂度 \(O(n \log n)\)。

#include <bits/stdc++.h>
using namespace std;
#define ls t[p].l
#define rs t[p].r
#define mid (l + r >> 1)
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define Next(i, u) for (int i = h[u]; i; i = e[i].next)
const int N = 500000 + 5;
const int Mod = 998244353;
struct tree { int sum, tag, l, r;} t[N * 40];
struct edge { int v, next;} e[N << 1];
int n, m, u, v, tot, cnt, h[N], rt[N], dep[N], val[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int Inc(int a, int b) { return (a += b) >= Mod ? a - Mod : a;}
int Dec(int a, int b) { return (a -= b) < 0 ? a + Mod : a;}
int Mul(int a, int b) { return 1ll * a * b % Mod;}
void add(int u, int v) {
e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
e[++tot].v = u, e[tot].next = h[v], h[v] = tot;
}
void Prefix(int u, int fa) {
dep[u] = dep[fa] + 1;
Next(i, u) if(e[i].v != fa) Prefix(e[i].v, u);
}
void down(int p) {
t[ls].sum = Mul(t[ls].sum, t[p].tag), t[rs].sum = Mul(t[rs].sum, t[p].tag);
t[ls].tag = Mul(t[ls].tag, t[p].tag), t[rs].tag = Mul(t[rs].tag, t[p].tag);
t[p].tag = 1;
}
void update(int &p, int l, int r, int x, int y, int k) {
if(!p) p = ++cnt, t[p].tag = 1; t[p].sum += k;
if(l == r) return;
if(mid >= x) update(ls, l, mid, x, y, k);
if(mid < y) update(rs, mid + 1, r, x, y, k);
}
void modify(int &p, int l, int r, int x, int y) {
if(!p) p = ++cnt, t[p].tag = 1;
if(l == r) { t[p].sum = 0; return;}
down(p);
if(mid >= x) modify(ls, l, mid, x, y);
if(mid < y) modify(rs, mid + 1, r, x, y);
t[p].sum = Inc(t[ls].sum, t[rs].sum);
}
void Merge(int &p, int k, int l, int r, int S1, int S2, int S) {
if(!p || !k) {
if(!p && !k) return;
if(!p) p = p + k, t[p].sum = Mul(t[k].sum, S1), t[p].tag = Mul(t[p].tag, S1);
else t[p].sum = Mul(t[p].sum, Inc(S2, S)), t[p].tag = Mul(t[p].tag, Inc(S2, S));
return ;
}
if(l == r) {
S1 = Inc(S1, t[p].sum), S2 = Inc(S2, t[k].sum);
int tmp = Mul(t[p].sum, t[k].sum);
t[p].sum = Inc(Mul(t[k].sum, S1), Mul(t[p].sum, Inc(S, S2)));
t[p].sum = Dec(t[p].sum, tmp);
return ;
}
down(p), down(k);
Merge(rs, t[k].r, mid + 1, r, Inc(S1, t[ls].sum), Inc(S2, t[t[k].l].sum), S);
Merge(ls, t[k].l, l, mid, S1, S2, S);
t[p].sum = Inc(t[ls].sum, t[rs].sum);
}
int query(int p, int l, int r, int x, int y) {
if(!p) return 0;
if(l >= x && r <= y) return t[p].sum;
down(p);
int ans = 0;
if(mid >= x) ans = Inc(ans, query(ls, l, mid, x, y));
if(mid < y) ans = Inc(ans, query(rs, mid + 1, r, x, y));
return ans;
}
void dfs(int u, int fa) {
update(rt[u], 0, n, val[u], val[u], 1);
Next(i, u) {
int v = e[i].v; if(v == fa) continue;
dfs(v, u), Merge(rt[u], rt[v], 0, n, 0, 0, t[rt[v]].sum);
}
modify(rt[u], 0, n, dep[u], dep[u]);
}
int main() {
n = read();
rep(i, 1, n - 1) u = read(), v = read(), add(u, v);
Prefix(1, 0);
m = read();
rep(i, 1, m) u = read(), v = read(), val[v] = max(val[v], dep[u]);
dfs(1, 0);
printf("%d", t[rt[1]].sum);
return 0;
}

当 \(dp\) 状态已经超过要求后,如果存在某一维初始值比较少,整体 \(dp\) 不失一个好的选择。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,992
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,506
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,349
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,134
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,767
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,844