显然直接计数是不好计的,只能从 \(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\) 不失一个好的选择。

[NOI2020]命运的更多相关文章

  1. P6773 [NOI2020]命运

    整体DP 很明显计算答案需要用容斥计算,如果暴力容斥的话,就是枚举哪些路径不符合条件,在这些路径的并集中的边都不能取,其他边任意取,设当前取了$i$条路径,那么对答案的贡献是$(-1)^i2^{n-1 ...

  2. DP 优化方法大杂烩 & 做题记录 I.

    标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...

  3. 初中的一些OI琐屑 & APIO2020 & NOI2020

    这篇文章会发布在我的博客上 https://www.cnblogs.com/dmoransky/(一个小习惯,把信息学竞赛的学习历程记录在个人博客中). 借这篇随笔回顾并简短总结一下我的初中OI(信息 ...

  4. 一个页面实例化两个ueditor编辑器,同样的出生却有不同的命运

    今天遇到一个比较怪异的问题,有一项目需要在同一个页面上展现两个ueditor编辑器,在展现时并不任何问题,但当点击了“保存”按钮时就出错了,有其中一个ueditor在asp.net中无法获取编辑器的值 ...

  5. HDU 2571 命运

    命运 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...

  6. HDUOJ----2571(命运)(简单动态规划)

    命运 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  7. HDU 2571 命运 动态规划

    命运 http://acm.hdu.edu.cn/showproblem.php?pid=2571 Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了!可谁能想到, ...

  8. HDU 2571 命运 (DP)

    命运 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Pr ...

  9. hdu2571 命运 动态规划Dp

    转载请注明出处:http://blog.csdn.net/u012860063 题目链接:pid=2571" target="_blank">http://acm. ...

随机推荐

  1. 洛谷1052——过河(DP+状态压缩)

    题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...

  2. Java GUI 桌面应用开发

    前言 虽然现在已经很少项目会涉及GUI技术,但作为一个合格的Java开发工程师,还是得了解才得 本文记录,idea使用JFormDesigner插件进行Java GUI 桌面应用开发 GUI Swin ...

  3. RotateRect(旋转矩形)的倾斜旋转变换矫正

    在Opencv中的图像处理中,经常要用到minAreaRect()函数求最小外接矩形,该函数的返回值就是一个RotatedRect类对象. RotatedRect类定义如下: class CV_EXP ...

  4. Android物联网应用程序开发(智慧园区)—— 登录界面开发

    效果: 布局代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:an ...

  5. 使用 windows bat 脚本命令 一键启动MySQL服务

    @echo off rem Copyright (c) 2019 Moses and/or its affiliates. rem Get Administrator Rights >nul 2 ...

  6. SpringBoot 之 实现登录功能及登录拦截器

    增加登录退出控制器: # src/main/java/com/wu/controller/LoginController.java @Controller public class LoginCont ...

  7. docker部署logstash

    1.下载镜像 [root@vanje-dev01 ~]# docker pull logstash:7.0.1 2.安装部署 2.1  创建宿主映射目录 # mkdir /etc/logstash/ ...

  8. 详谈 Java工厂 --- 抽象工厂模式

    1.前言 感觉工厂模式都好鸡肋,还特别绕来绕去,当然,好处还是有的,将一些类似的业务都集成到工厂了, 不需要理会底层是怎么运行的,我只需要向调用工厂即可获取我要的结果,也不需要考虑工厂返回的东西类型, ...

  9. jsp文件中文乱码解决

    文件顶加上 <%@ page contentType="text/html;charset=UTF-8" language="java" %>即可

  10. mysql数据库读写分离教程

    注意:实现MySQL读写分离的前提是我们已经将MySQL主从复制配置完毕    一.Mycat实现读写分离安装和配置 架构规划: 192.168.201.150 master 主节点 192.168. ...