P11363 [NOIP2024] 树的遍历
P11363 [NOIP2024] 树的遍历
题意
给你一个 \(n\) 个点的树,定义边与边相邻当且仅当它们有一个公共端点。一条合法 dfs 路径从某条边出发,如果存在未访问的相邻的边,则必须选择其中一条边走,否则回溯。
给你 \(k\) 个关键边,问以这些关键边为起点可以生成多少不同构的有标号无向边 dfs 树。
\(n \le 10^5\)。
思路
参考 @喵仔牛奶 的做法。
我们给边重新编号,边 \(u\) 表示点 \(u\) 连向父亲的那条边。假设原树以 \(1\) 为根,那么编号 \(\in[2,n]\)。由题目定义,\(u\) 与它的父亲 \(fa_u\),它的兄弟 \(bro_u\) 和它的儿子 \(son_u\) 相邻。以下直接写编号的都是指边,不是点。
当 \(k=1\) 时是好解决的,就是 \(\prod_{i=1}^n (d_i-1)!\)。其中 \(d_i\) 是点 \(i\) 的度数。
对于每个点,它所有边的访问顺序是一个排列,而当我们访问到点 \(u\) 的某条连边时,一定是由某条边过来的,那么由前面的操作我们钦定了这个排列的第一条边。然后点 \(u\) 可以自由决定其他边的访问顺序,所以就是 \(d_u-1\) 的连乘了。
当 \(k>1\) 时,问题是 \(k\) 个点出发可以生成多少不同构的树,也即有多少不同构的树可以以 \(k\) 个点其中一些点出发生成。容易想到容斥,但是首先要发掘更多性质。
我们想知道每个点的连边的排列和 dfs 树的关系。显然 dfs 树可能有同构,因此不是双射。
\(p_i\) 遍历完它的子树之后就会走到 \(p_{i+1}\)。因此 \(p_i\) 和 \(p_{i+1}\) 一定有连边!所以排列对 dfs 树上的一条链。
显然 reverse 排列不会改变 dfs 树。
那么我们可以容斥,计算有多少种合法的定排列头(定起点方向)的方案,然后如果某个点存在两个定起点方向的方案,它们成为排列头时的排列互为 reverse 的排列,dfs 树就重构了,需要减掉。计算起点边属于 \(k\) 个关键边条件下的方案。
因为每个点对应一个边的排列,所以我们按照点分析每个排列。
显然合法的点的连边的访问的排列顺序对应唯一的合法的 dfs 树,且对应唯一的出发边,但是合法的 dfs 树可以对应多个合法的每个点的连边访问排列顺序。因此考虑由 dfs 树推到哪些排列顺序然后推到哪些边可以做起点。
排列顺序只要一直往排列的第一条边的方向走就可以到达起点边了。换句话说,按照每个点的排列的最后一条边的方向画箭头,就可以得到以起点边为“根”的外向树。根必须是特殊边。
相当于要给每个排列选择两个端点,使得向着任意一个端点定向后是一棵外向树,且树根是特殊边。求排列方案数。
假如已经钦定了集合 \(S\) 的边可以作为树根,求出方案数。然后乘上 \((-1)^{|S|+1}\) 的容斥系数。
若边 \(x \in S\),\(x\) 将原树分成两部分,这两部分的点的排列的一个端点方向必须向 \(x\)。
因此所有 \(x \in S\) 一定在一条链上。
在这条链上的点(不含端点)的贡献是 \((d_u-2)!\),其他点是 \((d_u-1)!\)。可以变形成:
\]
前半是简单的,我们要计数后半部分。
进行树形 DP。按照点 DP。DP 的时候带上容斥系数。
设 \(c_i = \frac{1}{d_u-1}\)。
剩下的,和参考题解一样。
时间复杂度线性。
code
代码是不难写的,性质是很难找的。
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
constexpr int N=1e5+7,mod=1e9+7,Max=1e5;
int add(int a,int b) { return a+b>=mod ? a+b-mod : a+b; }
void _add(int &a,int b) { a=add(a,b); }
int mul(int a,int b) { return 1ll*a*b%mod; }
void _mul(int &a,int b) { a=mul(a,b); }
int testid,t,n,k;
int u,v;
typedef pair<int,int> pii;
#define fi first
#define se second
vector<pii> to[N];
int col[N];
int x;
int fac[N],inv[N];
void _init() {
fac[0]=1;
rep(i,1,Max) fac[i]=mul(fac[i-1],i), inv[i]=(i>1 ? mul(inv[mod%i],(mod-mod/i)) : 1);
}
int f[N];
int ans;
void init() {
rep(i,1,n) to[i].clear();
memset(col,0,sizeof(col));
ans=0;
}
void dfs(int u,int fa,int op) {
int w=inv[to[u].size()-1], s=mod-op;
f[u]=mod-op; _add(ans,op);
for(auto p : to[u]) {
int v=p.se, id=p.fi;
if(v^fa) {
dfs(v,u,col[id]);
_add(ans,mod-mul(w,mul(s,f[v]))), _add(f[u],mul((op ? 0 : f[v]),w)), _add(s,f[v]);
}
}
}
void main() {
sf("%d%d",&testid,&t);
_init();
while(t--) {
sf("%d%d",&n,&k);
init();
rep(i,1,n-1) {
sf("%d%d",&u,&v);
to[u].push_back({i,v});
to[v].push_back({i,u});
}
rep(i,1,k) {
sf("%d",&x);
col[x]=1;
}
dfs(1,0,0);
rep(i,1,n) _mul(ans,fac[to[i].size()-1]);
pf("%d\n",ans);
}
}
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
wing_heart :: main();
}
P11363 [NOIP2024] 树的遍历的更多相关文章
- 数据结构--树(遍历,红黑,B树)
平时接触树还比较少,写一篇博文来积累一下树的相关知识. 很早之前在数据结构里面学的树的遍历. 前序遍历:根节点->左子树->右子树 中序遍历:左子树->根节点->右子树 后序遍 ...
- YTU 3023: 树的遍历
原文链接:https://www.dreamwings.cn/ytu3023/2617.html 3023: 树的遍历 时间限制: 1 Sec 内存限制: 128 MB 提交: 3 解决: 2 题 ...
- 团体程序设计天梯赛-练习集L2-006. 树的遍历
L2-006. 树的遍历 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历 ...
- leetcode404-----简单的树的遍历
Find the sum of all left leaves in a given binary tree. Example: 3 / \ 9 20 / \ 15 7 There are two l ...
- pat L2-006. 树的遍历
L2-006. 树的遍历 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历 ...
- L2-006. 树的遍历
题目链接:L2-006. 树的遍历 今天一神给我手敲二叉树模板,瞬间就领悟了,感觉自己萌萌哒! 看上去很直观! #include <iostream> #include <cstdi ...
- js实现对树深度优先遍历与广度优先遍历
深度优先与广度优先的定义 首先我们先要知道什么是深度优先什么是广度优先. 深度优先遍历是指从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续 ...
- L2-006 树的遍历 (25 分) (根据后序遍历与中序遍历建二叉树)
题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805069361299456 L2-006 树的遍历 (25 分 ...
- 团体程序设计天梯赛 L2-006. 树的遍历 L2-011. 玩转二叉树
L2-006. 树的遍历 #include <stdio.h> #include <stdlib.h> #include <string.h> #include & ...
- L2-006 树的遍历 (后序中序求层序)
题目: 给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(≤30),是二叉树中结点的个数.第二行给出其后序遍历序 ...
随机推荐
- LNK1107 文件无效或损坏: 无法在 0x308 处读取 解决方案
LNK1107 文件无效或损坏: 无法在 0x308 处读取 解决方案 环境:windows 需要的库 freeglut.dll 使用cmake编译的时候,有一个选项,选择编译器,如果你编译的是一个3 ...
- C++ / java 风格指南
简介 https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/headers/#inline-funct ...
- opengl 学习 之 13 lesson
opengl 学习 之 13 lesson 简介 法向量纹理,让纹理显示的更逼真? link http://www.opengl-tutorial.org/uncategorized/2017/06/ ...
- OpenList挂载「PikPak」
存储->添加 选择PikPak 填写挂载路径 填写具体信息:用户名为自己 PikPak 的邮箱(登陆方式),密码为自己的密码. 获取刷新令牌 先在浏览器登录 PikPak 的 Web 端. 按 ...
- G. Sheba's Amoebas -ICPC North Central NA Contest 2017
https://nanti.jisuanke.com/t/43374 题意 判断像素矩阵中像素围成的封闭区域的个数 思路 对于每一个像素,检查周围是否有像素形成相连 从当前下一个未标记的像素开始进行搜 ...
- Vue实现登陆时token存放到Vuex
一.Vue实现登陆时token存放到Vuex 1.登录页面 <template> <div> <h1>登录</h1> {{ $store.state.t ...
- MyEMS开源能源管理系统核心代码解读022
MyEMS开源能源管理系统适用于建筑.工厂.商场.医院.园区的电.水.气等能源数据采集.分析.报表,还有光伏.储能.充电桩.微电网.设备控制.故障诊断.工单管理.人工智能优化等可选功能.资深专业团队开 ...
- Java面向对象基础——1.方法
目录 Java中的方法 方法的基本概念与作用 方法的定义与基本语法 各组成部分说明 实例方法与字段访问 方法的访问控制:public与private private方法的应用示例 this关键字的使用 ...
- 告别复杂配置!使用 1Panel 运行环境功能轻松搭建 Java 应用
一.引言:Java 部署之痛与新生 作为开发者,你是否经历过这些场景? 为不同Java版本频繁切换环境变量 在服务器手动安装Nginx或Tomcat配置端口 调试环境差异导致的"在我本地是好 ...
- 已解决This behaviour is the source of the following dependency conflicts.
https://blog.csdn.net/yuan2019035055/article/details/128733322