[NOI2020]命运
显然直接计数是不好计的,只能从 \(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\) 伸上来的所有链都会被满足,于是有转移:
\]
选 \(0\) 时,只需要考虑当前这个最深的链顶是否来自于 \(v\) 即可。
不来自于 \(v\) :
\]
来自于 \(v\) :
\]
需要注意深度相同时 \(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]命运的更多相关文章
- P6773 [NOI2020]命运
整体DP 很明显计算答案需要用容斥计算,如果暴力容斥的话,就是枚举哪些路径不符合条件,在这些路径的并集中的边都不能取,其他边任意取,设当前取了$i$条路径,那么对答案的贡献是$(-1)^i2^{n-1 ...
- DP 优化方法大杂烩 & 做题记录 I.
标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...
- 初中的一些OI琐屑 & APIO2020 & NOI2020
这篇文章会发布在我的博客上 https://www.cnblogs.com/dmoransky/(一个小习惯,把信息学竞赛的学习历程记录在个人博客中). 借这篇随笔回顾并简短总结一下我的初中OI(信息 ...
- 一个页面实例化两个ueditor编辑器,同样的出生却有不同的命运
今天遇到一个比较怪异的问题,有一项目需要在同一个页面上展现两个ueditor编辑器,在展现时并不任何问题,但当点击了“保存”按钮时就出错了,有其中一个ueditor在asp.net中无法获取编辑器的值 ...
- HDU 2571 命运
命运 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...
- HDUOJ----2571(命运)(简单动态规划)
命运 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- HDU 2571 命运 动态规划
命运 http://acm.hdu.edu.cn/showproblem.php?pid=2571 Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了!可谁能想到, ...
- HDU 2571 命运 (DP)
命运 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Pr ...
- hdu2571 命运 动态规划Dp
转载请注明出处:http://blog.csdn.net/u012860063 题目链接:pid=2571" target="_blank">http://acm. ...
随机推荐
- Essentially No Barriers in Neural Network Energy Landscape
目录 梗概 主要内容 path的定义 path的逼近 Mechanical Model Nudged Elastic Band 局部最优 Draxler F, Veschgini K, Salmhof ...
- Java EE数据持久化框架笔记 • 【目录】
章节 内容 实践练习 Java EE数据持久化框架作业目录(作业笔记) 第1章 Java EE数据持久化框架笔记 • [第1章 MyBatis入门] 第2章 Java EE数据持久化框架笔记 • [第 ...
- Java面向对象笔记 • 【第9章 JDBC编程】
全部章节 >>>> 本章目录 9.1 JDBC基础 9.1.1 JDBC简介 9.1.2 JDBC常用API简介 JDBC 常用API功能说明 9.1.3 JDBC编程步骤 ...
- UML的定义和组成详细介绍
目录 1.UML 1.1概述 1.2 UML是一种建模语言 1.3 UML语言包含三方面 2.UML支持软件体系结构建模 2.1 逻辑视图 2.2 实现(开发)视图 2.3 部署视图 2.4 过程视图 ...
- 记一次log4j2引发的渗透测试
前言 记一次log4j2打入内网并用CVE-2021-42287.CVE-2021-42278获取到DC权限的靶场渗透. 外网打点 首先对web进行端口扫描,发现38080端口和22端口 访问一下38 ...
- iNeuOS工业互联网操作系统,增加设备驱动的自定义参数模板,适配行业个性化设备的应用场景
目 录 1. 概述... 2 2. 平台演示... 2 3. 应用过程... 2 1. 概述 增加设备驱动的自定义参数模板,适配行业个性化设备的应用场景. ...
- SpringBoot集成jasperreport实现打印功能
1.jaspersoft studio工具下载地址 下载直通车 2.工具使用方法查看以下链接 工具使用 3.将工具编译后的.jasper文件放到SpringBoot项目的resources/templ ...
- GOF23种设计模式之单例模式(java)
GOF(group of four):四人帮 分类 创建者模式 单例模式 核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点 优点: 由于单例模式只生成一个实例,减少了系统性能开销, ...
- vue组件中的.sync修饰符使用
在vue的组件通信props中,一般情况下,数据都是单向的,子组件不会更改父组件的值,那么vue提供.sync作为双向传递的关键字,实现了父组件的变动会传递给子组件,而子组件的carts改变时,通过事 ...
- Yum安装Maven
一.安装 wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum. ...