[CF1111E]Tree
题目大意:给一棵$n(n\leqslant10^5)$个点的树,有$q(q\leqslant10^5)$次询问,每次询问给出$k,m,r$表示把以下$k$个点分成不超过$m$组,使得在以$r$为根的情况下,组内的任意两个结点不存在祖先关系。$\sum k\leqslant10^5,m\leqslant300$
题解:针对一次询问,可以想到一个$DP$,$f_{i,j}$表示处理到第$i$个点(假设为$u$),$u$的祖先都已经处理完,这$i$个点放到$j$组的方案数。$f_{i,j}=f_{i-1,j-1}+f_{i-1,j-father_u}$,$father_u$表示这$k$个点中$u$的祖先的个数。
现在考虑如何处理$DP$顺序,发现可以深度由浅到深处理,$dfs$序也是可以的。而后再考虑如何处理$father$,我想到了几种写法:
- 用树状数组,给子树加,然后查询需要的点,这种写法因为换根需要分类讨论,复杂度$O(k\log_2n)$。
- 树剖后单点加,询问需要的点到根的和(这种写法在写之前被我以为需要分类讨论),复杂度$O(k\log_2^2n)$。
- 建虚树,直接$dfs$,复杂度$O(k\log_2k)$。
我写的时候不想分类讨论,就选择了第$3$种,在我交的时候成功跑到$rank$倒一。写题解的时候突然发现这种写法似乎复杂度最优秀?自带大常数
卡点:树剖求$LCA$时$top$更新错
C++ Code:
#include <algorithm>
#include <cstdio>
#include <iostream>
#define maxn 100010
const int mod = 1e9 + 7;
inline void reduce(int &x) { x += x >> 31 & mod; } int head[maxn], cnt;
struct Edge {
int to, nxt;
} e[maxn << 1];
inline void addedge(int a, int b) {
e[++cnt] = (Edge) { b, head[a] }; head[a] = cnt;
e[++cnt] = (Edge) { a, head[b] }; head[b] = cnt;
} namespace Tree {
int sz[maxn], dep[maxn], fa[maxn];
int dfn[maxn], out[maxn], top[maxn], idx;
#define f top
int find(int x) { return x == f[x] ? x : (f[x] = find(f[x])); }
#undef f
void dfs(int u) {
int son = 0; top[u] = u;
dfn[u] = ++idx, sz[u] = 1;
for (int i = head[u], v; i; i = e[i].nxt) {
v = e[i].to;
if (v != fa[u]) {
dep[v] = dep[u] + 1, fa[v] = u;
dfs(v), sz[u] += sz[v];
if (sz[v] > sz[son]) son = v;
}
}
out[u] = idx; if (son) top[son] = u;
}
inline int LCA(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] > dep[top[y]]) x = fa[top[x]];
else y = fa[top[y]];
}
return dep[x] < dep[y] ? x : y;
}
}
using Tree::dfn;
using Tree::out; int n, q, k, m, r; int Mark[maxn], f[305], idx;
inline bool cmp(int x, int y) { return dfn[x] < dfn[y]; } void dfs(int u, int fa = 0, int num = 0) {
if (Mark[u]) {
++idx;
for (int i = std::min(m, idx); ~i; --i) {
if (i > num) f[i] = (static_cast<long long> (i - num) * f[i] + f[i - 1]) % mod;
else f[i] = 0;
}
++num;
}
for (int i = head[u], v; i; i = e[i].nxt) {
v = e[i].to;
if (v != fa) dfs(v, u, num);
} head[u] = Mark[u] = 0;
} void solve() {
static int List[maxn << 1], tot, S[maxn], top;
std::cin >> k >> m >> r;
for (int i = 0; i < k; ++i) {
std::cin >> List[i];
Mark[List[i]] = 1;
}
tot = k;
if (!Mark[r]) List[tot++] = r; std::sort(List, List + tot, cmp);
for (int i = tot - 1; i; --i) List[tot++] = Tree::LCA(List[i], List[i - 1]);
tot = (std::sort(List, List + tot, cmp), std::unique(List, List + tot) - List);
top = 0;
for (int I = 0, i = *List; I < tot; i = List[++I]) {
while (top && out[S[top]] < dfn[i]) --top;
if (top) addedge(S[top], i);
S[++top] = i;
} f[idx = 0] = 1, dfs(r);
int ans = 0;
for (int i = 1; i <= m; ++i) reduce(ans += f[i] - mod);
std::cout << ans << '\n'; cnt = 0;
__builtin_memset(f, 0, m + 1 << 2);
} int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
std::cin >> n >> q;
for (int i = 1, a, b; i < n; ++i) {
std::cin >> a >> b;
addedge(a, b);
}
Tree::dfs(1);
for (int i = 1; i <= n; ++i) Tree::find(i);
__builtin_memset(head, 0, n + 1 << 2), cnt = 0; while (q --> 0) solve();
return 0;
}
[CF1111E]Tree的更多相关文章
- CF1111E Tree 树链剖分,DP
CF1111E Tree 过年了,洛咕还没爬这次的题,先放个CF的链接吧. 补个LG传送门. 对于每个询问点\(x\),设它的祖先即不能和它放在同一个集合中的点的个数为\(f[x]\),设\(dp[i ...
- CF1111E Tree 动态规划+LCT
这个题的思路非常好啊. 我们可以把 $k$ 个点拿出来,那么就是求将 $k$ 个点划分成不大于 $m$ 个集合的方案数. 令 $f[i][j]$ 表示将前 $i$ 个点划分到 $j$ 个集合中的方案数 ...
- [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法
二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...
- SAP CRM 树视图(TREE VIEW)
树视图可以用于表示数据的层次. 例如:SAP CRM中的组织结构数据可以表示为树视图. 在SAP CRM Web UI的术语当中,没有像表视图(table view)或者表单视图(form view) ...
- 无限分级和tree结构数据增删改【提供Demo下载】
无限分级 很多时候我们不确定等级关系的层级,这个时候就需要用到无限分级了. 说到无限分级,又要扯到递归调用了.(据说频繁递归是很耗性能的),在此我们需要先设计好表机构,用来存储无限分级的数据.当然,以 ...
- 2000条你应知的WPF小姿势 基础篇<45-50 Visual Tree&Logic Tree 附带两个小工具>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000Things You Should Know About C# 和 2,0 ...
- Leetcode 笔记 110 - Balanced Binary Tree
题目链接:Balanced Binary Tree | LeetCode OJ Given a binary tree, determine if it is height-balanced. For ...
- Leetcode 笔记 100 - Same Tree
题目链接:Same Tree | LeetCode OJ Given two binary trees, write a function to check if they are equal or ...
- Leetcode 笔记 99 - Recover Binary Search Tree
题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...
随机推荐
- angular promise $q 异步调用
Angular异步调用 Promise和$q的用法 背景 首先说明一下promise异步调用出现的背景: javascript语言是一种单线程模式,就是说一次只能够执行一个任务,如果有多个任务的话就必 ...
- Maven学习(十三)-----Maven 构建生命周期
Maven 构建生命周期 构建生命周期是什么? 构建生命周期阶段的目标是执行顺序是一个良好定义的序列. 这里使用一个例子,一个典型的 Maven 构建生命周期是由下列顺序的阶段: 阶段 处理 描述 准 ...
- Struts2(一.基本介绍,环境搭建及需求分析)
Struts2框架开发 前言 开发工具:eclipse struts1:老项目使用较多,维护时需要用到 struts2:新项目使用较多 一.特点 1. 无侵入式设计 struts2 与 struts ...
- Parcel 打包器简单使用记录
本文是构造 UI 轮子过程中搭建项目初始化时使用 Parcel 作为打包器的简要使用记录. 安装 参考 官方文档 使用 npm 进行 parcel-bundler 的安装. npm i -D parc ...
- 【ZABBIX】SNMPtrap实现主动监控的原理与安装配置
工欲善其事,必先利其器.作为一款强大的开源软件,Zabbix号称“Monitor Everything”,其所依赖的,很大程度上便是SNMP的数据采集支持.SNMP 协议是用来管理设备的协议,目前SN ...
- 欢迎来怼--第二十三次Scrum会议
一.小组信息 队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/11/11 17:20~17:55,总计35min. 地 ...
- c# dictionnary根据value查找对应的key
属性方法中并没有包含此功能,因此需要自己自定义一个方法: string regionName = ""; if (ControlForm.swichLanguage.Contain ...
- 用python脚本计算某一个文件的行数
python可以统计文件的行数,你相信吗?不管你信不信反正我信了.下面我们来看一下python怎样统计文件的行数,代码很简单,我也做了注释,很简单的实现... 1 2 3 4 5 6 7 8 9 10 ...
- C语言问卷调查表
你对自己的未来有什么规划?做了哪些准备? 对未来比较迷茫,现在的主要任务是学好专业课 你认为什么是学习?学习有什么用?现在学习动力如何?为什么? 活到老学到老,学习是一辈子的事.在学习的过程 ...
- lintcode-107-单词切分
107-单词切分 给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词. 样例 给出 s = "lintcode" dict = [" ...