显然直接计数是不好计的,只能从 \(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. Essentially No Barriers in Neural Network Energy Landscape

    目录 梗概 主要内容 path的定义 path的逼近 Mechanical Model Nudged Elastic Band 局部最优 Draxler F, Veschgini K, Salmhof ...

  2. Java EE数据持久化框架笔记 • 【目录】

    章节 内容 实践练习 Java EE数据持久化框架作业目录(作业笔记) 第1章 Java EE数据持久化框架笔记 • [第1章 MyBatis入门] 第2章 Java EE数据持久化框架笔记 • [第 ...

  3. Java面向对象笔记 • 【第9章 JDBC编程】

    全部章节   >>>> 本章目录 9.1 JDBC基础 9.1.1 JDBC简介 9.1.2 JDBC常用API简介 JDBC 常用API功能说明 9.1.3 JDBC编程步骤 ...

  4. UML的定义和组成详细介绍

    目录 1.UML 1.1概述 1.2 UML是一种建模语言 1.3 UML语言包含三方面 2.UML支持软件体系结构建模 2.1 逻辑视图 2.2 实现(开发)视图 2.3 部署视图 2.4 过程视图 ...

  5. 记一次log4j2引发的渗透测试

    前言 记一次log4j2打入内网并用CVE-2021-42287.CVE-2021-42278获取到DC权限的靶场渗透. 外网打点 首先对web进行端口扫描,发现38080端口和22端口 访问一下38 ...

  6. iNeuOS工业互联网操作系统,增加设备驱动的自定义参数模板,适配行业个性化设备的应用场景

    目       录 1.      概述... 2 2.      平台演示... 2 3.      应用过程... 2 1.   概述 增加设备驱动的自定义参数模板,适配行业个性化设备的应用场景. ...

  7. SpringBoot集成jasperreport实现打印功能

    1.jaspersoft studio工具下载地址 下载直通车 2.工具使用方法查看以下链接 工具使用 3.将工具编译后的.jasper文件放到SpringBoot项目的resources/templ ...

  8. GOF23种设计模式之单例模式(java)

    GOF(group of four):四人帮 分类 创建者模式 单例模式 核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点 优点: 由于单例模式只生成一个实例,减少了系统性能开销, ...

  9. vue组件中的.sync修饰符使用

    在vue的组件通信props中,一般情况下,数据都是单向的,子组件不会更改父组件的值,那么vue提供.sync作为双向传递的关键字,实现了父组件的变动会传递给子组件,而子组件的carts改变时,通过事 ...

  10. Yum安装Maven

    一.安装 wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum. ...