2022天梯赛 L3_2 关于深度优先搜索和逆序对的题应该不会很难吧这件事 【树上逆序对计数】

传送门:https://pintia.cn/problem-sets/994805046380707840/exam/problems/1518582895035215872?type=7&page=1

1
2

思路

观察发现,逆序对可以分成两类

  1. 节点 u u u v v v 有明确的父子关系(不一定是直属的直连边,可能是儿子往上跳很多条边才到达父亲),假设 u u u v v v 的父亲,那么不管怎么遍历, u u u 一定会比 v v v遍历到,因为 v v v u u u 的子树里;那么这种情况下,如果 u > v u > v u>v 的话,就会产生一个逆序对
  2. 节点 u u u v v v 没有明确的父子关系,那么假设它们的 L C A LCA LCA L L L u u u v v v 的出现次序取决于 L L L 的遍历顺序,因此这一部分的逆序对数量显然和 L L L直属儿子数 的 全排列 有关

对于第一部分的答案,我们很容易就可以统计,进入一个点 u u u 时,我们在 u u u 这个位置打上标记,那么之后所有在 u u u 的子树中的点 v v v,都会被这个标记影响,对于当前点 v v v,要统计其有多少个父亲的点权大于它(产生逆序对),只需要使用树状数组查询 [ v + 1 , n ] [v + 1, n] [v+1,n] 的和即可;离开 u u u 时,我们删除这个标记即可

假设我们求出来第一部分产生的逆序对为 c n t cnt cnt 个,不管 d f s dfs dfs 序如果变化,这第一部分产生的逆序对是恒定不变的
一共有 ∏ s o n i ! \prod son_i ! soni! d f s dfs dfs 序( s o n i son_i soni 表示节点 i i i直属儿子数量), s o n i > 0 son_i > 0 soni>0
所以第一部分的答案是: c n t × ∏ s o n i ! cnt \times \prod son_i ! cnt×soni!

对于第二部分的答案,我们对于当前节点 u u u,假设 s z [ v ] sz[v] sz[v] 为点 v v v 的子树中的节点数量,那么 子树 u u u 中,以 u u u L C A LCA LCA 的点对两两配对的方案数有: s z [ v 1 ] × s z [ v 2 ] + s z [ v 1 ] × s z [ v 3 ] + . . . + s z [ v k − 1 ] × s z [ v k ] (假设 u 有 k 个儿子) sz[v_1] \times sz[v_2] + sz[v_1] \times sz[v_3] +... + sz[v_{k-1}] \times sz[v_k](假设 u 有 k 个儿子) sz[v1]×sz[v2]+sz[v1]×sz[v3]+...+sz[vk1]×sz[vk](假设uk个儿子),就是将 u u u 的所有儿子的子树大小两两相乘,从而算出不在同一颗子树的点对数量;
对于某个点对 ( x , y ) (x,y) (x,y) x , y x,y x,y 满足某种大小关系,假设 x < y x < y x<y,那么当 x x x 位于的子树比 y y y 位于的子树后遍历到时,这个点对会产生一个逆序对,
而对于 u u u 的直属儿子的全排列,从期望或者概率的角度出发,一定是恰好有一半的情况使得 x x x y y y 前面,而剩下一半的情况 x x x y y y 的后面,所以我们只需要将 k ! ÷ 2 k! \div 2 k!÷2 就是产生逆序对的情况数( k k k u u u 的直属儿子数量)
但是,除了当前 u u u 的直属儿子遍历顺序会变,其他节点的遍历顺序也会变,全部累乘起来的话,其实和第一部分的 ∏ s o n i \prod son_i soni 是一样的
因此对于当前节点 u u u,其不同子树内贡献的逆序对数量为: ∏ s o n i 2 × s u m \dfrac {\prod son_i}{2} \times sum 2soni×sum s u m sum sum 代表 u u u 中不同子树内的点对数量,对于不同的 u u u,可以将 ∏ s o n i 2 \dfrac {\prod son_i}{2} 2soni 提公因子出来,只累加 s u m sum sum 到最后算即可
关于 u u u 的来自不同子树内的点对数量,可以简单地使用类似前缀和来解决,详情看代码 d f s 0 dfs0 dfs0 的部分

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
#define lowbit(x) ((x) & -(x))

const int INF=0x3f3f3f3f;
const long long INFLL=1e18;

typedef long long ll;

template<class T>
constexpr T power(T a, ll b){
    T res = 1;
    while(b){
        if(b&1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
    ll res = a * b - ll(1.L * a * b / mod) * mod;
    res %= mod;
    if(res < 0) res += mod; //误差
    return res;
}

template<ll P>
struct MLL{
    ll x;
    constexpr MLL() = default;
    constexpr MLL(ll x) : x(norm(x % getMod())) {}

    static ll Mod;
    constexpr static ll getMod(){
       if(P > 0) return P;
       return Mod;
    }

    constexpr static void setMod(int _Mod){
       Mod = _Mod;
    }
    constexpr ll norm(ll x) const{
       if(x < 0){
           x += getMod();
       }
       if(x >= getMod()){
           x -= getMod();
       }
       return x;
    }
    constexpr ll val() const{
       return x;
    }
    explicit constexpr operator ll() const{ 
       return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)
    }
    constexpr MLL operator -() const{ //负号,等价于加上Mod
       MLL res;
       res.x = norm(getMod() - x);
       return res;
    }
    constexpr MLL inv() const{
       assert(x != 0);
       return power(*this, getMod() - 2); //用费马小定理求逆
    }
    constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象
       x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
       return *this;
    }
    constexpr MLL& operator += (MLL rhs) & {
       x = norm(x + rhs.x);
       return *this;
    }
    constexpr MLL& operator -= (MLL rhs) & {
       x = norm(x - rhs.x);
       return *this;
    }
    constexpr MLL& operator /= (MLL rhs) & {
       return *this *= rhs.inv();
    }
    friend constexpr MLL operator * (MLL lhs, MLL rhs){
       MLL res = lhs;
       res *= rhs;
       return res;
    }
    friend constexpr MLL operator + (MLL lhs, MLL rhs){
       MLL res = lhs;
       res += rhs;
       return res;
    }
    friend constexpr MLL operator - (MLL lhs, MLL rhs){
       MLL res = lhs;
       res -= rhs;
       return res;
    }
    friend constexpr MLL operator / (MLL lhs, MLL rhs){
       MLL res = lhs;
       res /= rhs;
       return res;
    }
    friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
       ll v;
       is >> v;
       a = MLL(v);
       return is;
    }
    friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
       return os << a.val();
    }
    friend constexpr bool operator == (MLL lhs, MLL rhs){
       return lhs.val() == rhs.val();
    }
    friend constexpr bool operator != (MLL lhs, MLL rhs){
       return lhs.val() != rhs.val();
    }
};

const ll mod = 1e9 + 7;
using Z = MLL<mod>;

struct Comb {
	int n;
	std::vector<Z> _fac;
	std::vector<Z> _invfac;
	std::vector<Z> _inv;

	Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
	Comb(int n) : Comb() {
		init(n);
	}

	void init(int m) {
		m = std::min(1ll * m, Z::getMod() - 1);
		if (m <= n) return; //已经处理完了需要的长度
		_fac.resize(m + 1);
		_invfac.resize(m + 1);
		_inv.resize(m + 1);

		for (int i = n + 1; i <= m; i++) {
			_fac[i] = _fac[i - 1] * i;
		}
		_invfac[m] = _fac[m].inv();
		for (int i = m; i > n; i--) { //线性递推逆元和阶乘逆元
			_invfac[i - 1] = _invfac[i] * i;
			_inv[i] = _invfac[i] * _fac[i - 1];
		}
		n = m; //新的长度
	}

	Z fac(int m) {
		if (m > n) init(2 * m);
		return _fac[m];
	}
	Z invfac(int m) {
		if (m > n) init(2 * m);
		return _invfac[m];
	}
	Z inv(int m) {
		if (m > n) init(2 * m);
		return _inv[m];
	}
	Z binom(int n, int m) { //二项式系数
		if (n < m || m < 0) return 0;
		return fac(n) * invfac(m) * invfac(n - m);
	}
} comb;

const int N = 300050;

int fen[N]; //树状数组
std::vector<int> g[N];
Z k = 1; //dfs序数量
Z cnt = 0;
Z sum = 0;
int n, root;
int sz[N]; //子树大小

int query(int p){
    int res = 0;
    while(p > 0){
        res += fen[p];
        p -= lowbit(p);
    }
    return res;
}

void update(int p, int d){
   while(p <= n){
      fen[p] += d;
      p += lowbit(p);
   }
}
void dfs0(int u, int fa){
	cnt += query(n) - query(u);
	update(u, 1); //进入这个点,加上影响
	int num = (g[u].size() - 1); //儿子数,减去父亲那条边
	if(u == root) ++num; //根节点没有父亲
	if(num > 0) k *= comb.fac(num); //排除叶子节点
	sz[u] = 1;
	Z s = 0; //子树大小前缀和
	for(auto v : g[u])
		if(v ^ fa){
			dfs0(v, u);
			sz[u] += sz[v];
			sum += sz[v] * s;
			s += sz[v];
		}
	update(u, -1); //离开这个点,删去影响
}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    std::cin >> n >> root;
    fore(i, 1, n){
        int u, v;
        std::cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs0(root, 0);
	Z ans = k * cnt + k * sum / 2;
	std::cout << ans;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/556687.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

今天给大家推荐36套404页面模板

404页面是网站必备的一个页面&#xff0c;它承载着用户体验与SEO优化的重任。当用户访问不存在的页面时&#xff0c;服务器会返回404错误代码&#xff0c;并显示404页面。一个好的404页面可以帮助用户快速找到所需信息&#xff0c;并提升网站的用户体验。 以下是一些演示下载资…

Web3技术简介:重新定义互联网的未来

引言 在21世纪的数字时代&#xff0c;互联网已成为我们日常生活的不可或缺的一部分。然而&#xff0c;随着区块链和加密技术的快速发展&#xff0c;一个全新的互联网模型——Web3&#xff0c;正逐渐崭露头角。Web3不仅仅是技术的进步&#xff0c;它更是对传统互联网模型的挑战…

类和对象中阶3⃣️-默认成员函数(赋值运算符重载,取地址及 const取地址操作符重载等)

目录 5.赋值运算符重载 5.1 运算符重载 5.2 赋值运算符重载 5.3 前置和后置重载 5.4 重载流插入与流提取 流插入<<运算符重载 流提取运算符重载 6.日期类实现 7.const成员 8.取地址 及 const取地址操作符 重载 5.赋值运算符重载 5.1 运算符重载 C为了增强代码…

数仓建模—数据仓库初识

数仓建模—数据仓库初识 数据仓库之父Bill Inmon在1991年出版的"Building the Data Warehouse"一书中所提出的定义被广泛接受 数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的&#xff08;Subject Oriented&#xff09;、集成的&#xff08;Integ…

Mysql索引专题

文章目录 1. 数据库索引结构1.1 Hash结构1.2 树结构1.3 Mysql索引怎么提升效率? 2. 执行计划 explainidselect_typetabletypepossible_keyskeykey_lenrefrowsfiteredextra 1. 数据库索引结构 我们都知道mysql数据库的常用存储结构是B树&#xff0c;为什么是B树&#xff1f;试…

邮件代发API发送邮件如何使用?操作指南?

邮件代发邮箱API发送邮件的步骤&#xff1f;代发有哪些注意事项&#xff1f; 在自动化办公、批量营销等场景中&#xff0c;手动发送邮件往往显得效率低下&#xff0c;这时候&#xff0c;邮件代发API就显得尤为重要。那么&#xff0c;邮件代发API发送邮件究竟如何使用呢&#x…

买婴儿洗衣机怎么选择?四大绝佳好用婴儿洗衣机分享

幼龄时期的宝宝的衣物&#xff0c;是比较需要注意的时候。可能一不注意宝宝穿在身上就会有不适宜症状发生。所以宝妈们真的要随时观察&#xff0c;然后在宝宝洗衣服的这上面多下点功夫&#xff0c;不要让宝宝受到这种无谓的伤害。小婴儿的抵抗力比我们差很多。有些细菌、病毒可…

Hadoop大数据处理技术-Linux相关命令

​7.Linux常用命令 1&#xff09;Windows中的dir&#xff1a;列出当前目录下所有的文件和目录 2&#xff09;cd&#xff1a;改变当前目录 cd命令并不能直接实现这种跳跃转换目录的功能 它只能让你在当前目录和其子目录之间来回切换 就像在一张平面地图上移动一样 如果想跨目录…

如何挑选护眼灯?分享护眼灯排行榜前十名

许多家长肯定都有这样的烦恼&#xff0c;家中的孩子自从上学后&#xff0c;每天回家后的学习作写阅读时总会在不知不觉间越来越贴近书本&#xff0c;后来还会时不时眯着眼睛看东西&#xff0c;但其实这种用眼习惯的最大原因是孩子没有足够光线和舒适的环境光线导致的&#xff0…

硬件设备杂记——12G SDI及 AES67/EBU

常见的 SDI线缆规格&#xff0c;HD-SDI又被称为1.5G-SDI&#xff0c;具体参数以秋叶原的参数为例 AES67/EBU 目前音频网络标准主要集中在OSI网络体系的第二层和第三层。 第二层音频标准的弊端在于构建音频网络时需要专用的交换机&#xff0c;无法利用现有的以太网络&#xff0c…

SpringBoot基于redis zset实现滑动窗口限流

通过Redis zset实现滑动窗口限流算法 在开发高并发系统时有三把利器用来保护系统&#xff1a;缓存、降级和限流。限流可以认为服务降级的一种&#xff0c;限流通过限制请求的流量以达到保护系统的目的。 一般来说&#xff0c;系统的吞吐量是可以计算出一个阈值的&#xff0c;…

【leetcode面试经典150题】59. 合并两个有序链表(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

【Java框架】Spring框架(四)——Spring中的Bean的创建与生命周期

目录 SpringBean的创建步骤后置处理器(PostProcessor)BeanFactoryPostProcessorBeanPostProcessorInstantiationAwareBeanPostProcessorpostProcessBeforeInstantiationpostProcessAfterInstantiationpostProcessProperties SmartInstantiationAwareBeanPostProcessordetermine…

空心电抗器的matlab建模与性能仿真分析

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 空心电抗器是一种无铁芯的电感元件&#xff0c;主要由一圈或多圈导线绕制在非磁性材料制成的空心圆筒或其他形状的骨架上构成。其工作原理基于法拉第电磁感应定律&#xff0c;…

Maui 开始笔记

1&#xff0c;仿真器硬件加速&#xff0c;需要安装 2&#xff0c;刚创建的maui 不添加的话&#xff0c;启动可能时会自动退出&#xff0c;不退出&#xff0c;可以不加次配置 MauiApp1.csproj 文件中配置 在 PropertyGroup 元素下添加 <WindowsAppSdkDeploymentManagerIniti…

【Qt】常用控件(LCD Number/进度条/日历)

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、LCD Number(LCD显示器) 一个倒计时程序 二、ProgressBar(进度条) 1、创建一个进度条&#xff0c;100ms进度增加…

hv第一坑:定时器

错误代码 重试策略&#xff1a;一次延迟1s,最长30s直至事件成功。 int try_count 0;//do something if(not success)m_loop->setTimerInLoop((try_count > 30 ? 30: try_count) *1000 , cb, INFINITE, 0x100);表现现象 cpu 爆了内存爆了 总结原因 hv内部代码bug&…

C语言—常用字符串函数剖析

字符串函数 cplusplus.com/reference/cstring/ 更多没有总结到的函数大家可以自行查阅 这篇文章只是把最需要知道的函数做一个总结 strlen size_t strlen ( const char * str );字符串已经 ‘\0’ 作为结束标志&#xff0c;strlen函数返回的是在字符串中 ‘\0’ 前面出现的…

力扣面试150 文本左右对齐 贪心 字符串 满注释版

Problem: 68. 文本左右对齐 思路 &#x1f469;‍&#x1f3eb; 三叶题解 &#x1f496; Code class Solution { public List<String> fullJustify(String[] words, int maxWidth){List<String> ans new ArrayList<>();// 结果List<String> list …