P11363 [NOIP2024] 树的遍历 题解
思路:
从简单情况考虑,所以考虑当 \(k = 1\) 时的答案。可以发现,当我们有了一个关键边之后,到达每个点的边都是确定的,并且,这个点的邻边在新树上会构成一条链,所以除了进入这个点的边以外,其他的邻边可以任意安排顺序,所以这个点的贡献就是 \((deg_u - 1)!\) 。所以总贡献就是:
\]
再看到部分分,考虑 \(k=2\) 的情况,一个直接的想法是将 \(k=1\) 时的答案乘以\(2\),但是这是错的,通过样例2解释你就会发现,这种情况可能会有重复的计数出现。所以考虑能同时满足两个关键点生成的树有什么特点:显然的,这两个关键边之间会形成一条链,自己画一下图就可以发现,链上的每一个点不仅入边确定了,出边也是确定的,这样才能满足这颗树会同时出现在第一个关键边和第二个关建边的贡献内。所以链上的每个点的贡献就为 \((deg_u-2)!\) ,所以令这条链为 \(L\) (不包括链的两个端点)。 所以形式化的,重复的树的数量就为:
\]
这就促使我们用容斥原理解决这道题了,钦定一个关键边集 \(S\) ,求可以同时以这些关键边为根的新树个数。个数记为 \(g(S)\) ,所以最终答案为:
\]
那么现在考虑一般情况,可以发现的是,如果以某个点为根有至少3个子树存在钦定关键边,那么重复的数量就为\(0\)。也就是我们容斥中钦定的关键边必然构成原树上的一条链才是有有效贡献的。
如果我们尝试去确定这条链,你会发现每一条链的贡献最终形式化的表达就是当 \(k=2\) 时的那个式子。
我们考虑把 \(\prod_{u\in T}(deg_u-1)!\) 这部分提出来最后乘。所以此时就可以记点\(u\)的点权为:\(c_u=(deg_u-1)^{-1}\),所以一条链的贡献就是这条链上的点权之积。定义 \(f_u\) 表示钦定的关键点集构成的链经过 \(u\) ,且恰好其中一个端点在 \(u\) 子树内(包括\(u\))的带权方案数(也就是所有可能的链的方案和)。我们分情况转移(注意转移的时候我们先不考虑容斥系数指数上面多出来的\(+1\),留到最后统计答案的时候处理):
1. 若边 \((u,fa_u)\) 可以作为钦定的关键边,所以转移贡献可以由一下几部分加起来组成:只选 \((u,fa_u)\),只选子树内的链 \(c_u\sum_vf_v\),两者同时选 \(-c_u\sum_vf_v\),所以总贡献就为\(-1\)。
2. 若不是上述情况,就只能只选子树内的链,贡献为 \(c_u\sum_vf_v\)。
所以形式化的:
\begin{cases}
-1,\left(if\ (u,fa_u)\ is\ key \ edge\right)\\
c_u\sum_vf_v\\
\end{cases}
\]
现在考虑如何统计答案。
朴素的方法是合并子树,即对于点\(u\),我们考虑枚举其子树,将子树中的方案乘起来,然后合并枚举到的子树继续处理。设 \(s\) 为当前遍历到的子树贡献和,所以形式化的:
若\((u,fa_u)\)可以作为关键边,那么先让 \(ans\leftarrow ans+1\) (只选这条边),\(s=-1\)。
接着枚举儿子 \(v\) :
\]
\]
枚举儿子贡献 \(ans\) 的时候是减法是因为我们把容斥系数上面的 \(-1\) 留到了贡献答案的时候来处理。
总时间复杂度 \(O(Tn)\) 。
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define int long long
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch > '9' || ch < '0'){if(ch == '-'){f = -1;}ch = getchar();}
while(ch >= '0'&&ch <= '9'){x = x * 10 + ch - 48; ch = getchar();}
return x * f;
}
const int N = 1e5 + 10;
const int MOD = 1e9 + 7;
int n,k,fac[N],inv[N],vis[N],ans,f[N];
std::vector<std::pair<int,int> > G[N];
void init()
{
fac[0] = 1;
for(register int i = 1;i < N;++i) fac[i] = fac[i - 1] * i % MOD;
inv[0] = inv[1] = 1;
for(register int i = 2;i < N;++i){
inv[i] = MOD - 1ll * (MOD / i) * inv[MOD % i] % MOD;
}
}
void dfs(int u,int fa,int fl)
{
int s = -fl;
f[u] = -fl;
ans += fl;
int cost = inv[G[u].size() - 1];
for(auto tmp : G[u])
{
int v = tmp.first,id = tmp.second;
if(v == fa) continue;
dfs(v,u,vis[id]);
f[u] = (f[u] + (fl ? 0 : f[v]) * cost % MOD + MOD) % MOD;
ans = (ans - ((cost * s % MOD) * f[v] % MOD) + MOD) % MOD;
s = (s + f[v] + MOD) % MOD;
}
}
signed main()
{
int c,T;
c = read();
T = read();
init();
while(T--)
{
n = read();
k = read();
for(int i = 1;i < n;++i)
{
int u,v;
u = read();
v = read();
G[u].emplace_back(v,i);
G[v].emplace_back(u,i);
}
for(int i = 1;i <= k;++i)
{
int id;
id = read();
vis[id] = 1;
}
dfs(1,0,0);
for(int i = 1;i <= n;++i) ans = (ans * fac[G[i].size() - 1]) % MOD;
std::cout << ans << '\n';
for(int i = 1;i <= n;++i){
G[i].clear();
vis[i] = 0;
}
ans = 0;
}
return 0;
}
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),是二叉树中结点的个数.第二行给出其后序遍历序 ...
随机推荐
- Spring扩展接口-BeanPostProcessor
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...
- python爬虫学习——xlwt库,sqlite库
xlwt库主要是对excel进行操作,主要流程就是创建对象,创建工作表,写入数据,保存数据表.代码练习如下 ''' import xlwt workbook = xlwt.Workbook(encod ...
- SwanLab入门深度学习:Qwen3大模型指令微调
一.概述 Qwen3是通义千问团队的开源大语言模型,由阿里云通义实验室研发.以Qwen2作为基座大模型,通过指令微调的方式实现高准确率的文本分类,是学习大语言模型微调的入门任务. 指令微调是一种通过在 ...
- 获取Harbor镜像仓库指定Project的容量使用并进行企业微信告警
简单说明 在镜像仓库的维护中,有时我们需要根据镜像仓库的使用情况进行及时的告警和扩容,避免镜像仓库容量满载时再进行扩容,这样会造成业务的阻塞. 这里我们使用Python简单写一个实现获取镜像仓库指定项 ...
- C# WinForms 实现打印监听组件
一.组件简介 打印监听组件是一款集成于 Windows 桌面环境的打印任务管理与监控工具,适用于企业级应用场景.它不仅支持多打印机任务的实时监控,还能通过 WebSocket 与外部系统集成,实现自动 ...
- 【实战】基于 Tauri 和 Rust 实现基于无头浏览器的高可用网页抓取
一.背景 在 Saga Reader 的早期版本中,存在对网页内容抓取成功率不高的问题.主要原因是先前采用的方案为后台进程通过 reqwest 直接发起 GET 请求获取网站 HTML 的方案,虽然仿 ...
- 指标管理+AI大模型深度融合,开启智能数据分析管理新时代
随着企业数字化转型的加速,数据管理和分析变得越来越重要.传统的指标管理平台虽然已经能够帮助企业有效地收集.计算.管理和展示关键指标,但在业务分析层面,面对日益复杂的数据环境和业务需求,单纯依靠人工分析 ...
- Hyperworks对比其他仿真软件有哪些特色_Hyperworks介绍
HyperWorks是一套集成的CAE(计算机辅助工程)解决方案,由Altair公司开发.与其他仿真软件相比,HyperWorks具有以下特色: 开放性和灵活性:HyperWorks采用开放式架构,允 ...
- Vertx 实现webapi实战项目(二)
消息解析:消息序列化和反序列化---上传json解析和返回json编码. 整理下工程项目 一:实现消息接口,在imp文件夹下新建接口MessageFactory 1 /****** 2 * 消息编 ...
- ui-router 路由重定向
$urlRouterProvider .when('/c?id', '/contacts/:id') .when('/user/:id', '/contacts/:id') .otherwise('/ ...