PTA L3-032 关于深度优先搜索和逆序对的题应该不会很难吧这件事 (30 分)

该博客介绍了深度优先搜索算法(DFS)及其在树上的应用,特别是在计算树的所有可能DFS序中逆序对数量之和的问题。通过DFS遍历树,并使用辅助数据结构动态维护逆序对数量,最终对答案取模给出结果。示例展示了输入输出格式以及具体解题代码。
摘要由CSDN通过智能技术生成

PTA L3-032 关于深度优先搜索和逆序对的题应该不会很难吧这件事 (30 分)

背景知识
深度优先搜索与 D F S DFS DFS
深度优先搜索算法 ( D F S ) (DFS) DFS是一种用于遍历或搜索树或图的算法。
以下伪代码描述了在树 T T T 上进行深度优先搜索的过程:

procedure DFS(T, u, L)      // T 是被深度优先搜索的树
                            // u 是当前搜索的节点
                            // L 是一个链表,保存了所有节点被第一次访问的顺序
  append u to L             // 将节点 u 添加到链表 L 的末尾
  for v in u.children do    // 枚举节点 u 的所有子节点 v
    DFS(T, v)               // 递归搜索节点 v

r r r 为树 T T T 的根,调用 D F S ( T , r , L ) DFS(T,r,L) DFS(T,r,L) 即可完成对 T T T 的深度优先搜索,保存在链表 L L L 中的排列被称为 D F S DFS DFS 序。
相信聪明的你已经发现了,如果枚举子节点的顺序不同,最终得到的 D F S DFS DFS 序也会不同。

逆序对
给定一个长度为 n n n 的整数序列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,该序列的逆序对数量是同时满足以下条件的有序数对 ( i , j ) (i,j) (i,j) 的数量:

  • 1 ≤ i < j ≤ n 1≤i<j≤n 1i<jn
  • a i > a j a_i>a_j ai>aj

问题求解
给定一棵 n n n 个节点的树,其中节点 r r r 为根。
求该树所有可能的 D F S DFS DFS 序中逆序对数量之和。

输入格式
第一行输入两个整数 n , r , n,r, nr表示树的大小与根节点。
对于接下来的 ( n − 1 ) (n−1) (n1) 行,第 i i i 行输入两个整数 u i u_i ui v i v_i vi,表示树上有一条边连接节点 u i u_i ui v i v_i vi

输出格式
输出一行一个整数,表示该树所有可能的 D F S DFS DFS 序中逆序对数量之和。
由于答案可能很大,请对 1 0 9 + 7 10^9+7 109+7 取模后输出。

数据范围
2 ≤ n ≤ 3 × 1 0 5 , 2≤n≤3×10^5, 2n3×105,
1 ≤ r ≤ n , 1≤r≤n, 1rn,
1 ≤ u i , v i ≤ n 。 1≤u_i,v_i≤n。 1ui,vin

输入样例1:

5 3
1 5
2 5
3 5
4 3

输出样例1:

24

输入样例2:

10 5
10 2
2 5
10 7
7 1
7 9
4 2
3 10
10 8
3 6

输出样例2:

516

样例解释
下图展示了样例 1 1 1 中的树。
在这里插入图片描述
该树共有 4 4 4 种可能的 D F S DFS DFS 序:

  • 3 , 4 , 5 , 1 , 2 , 有 6 个 逆 序 对 ; {3,4,5,1,2},有 6 个逆序对; 3,4,5,1,26
  • 3 , 4 , 5 , 2 , 1 , 有 7 个 逆 序 对 ; {3,4,5,2,1},有 7 个逆序对; 3,4,5,2,17
  • 3 , 5 , 1 , 2 , 4 , 有 5 个 逆 序 对 ; {3,5,1,2,4},有 5 个逆序对; 3,5,1,2,45
  • 3 , 5 , 2 , 1 , 4 , 有 6 个 逆 序 对 。 {3,5,2,1,4},有 6 个逆序对。 3,5,2,1,46

因此答案为 6 + 7 + 5 + 6 = 24 6+7+5+6=24 6+7+5+6=24

#include<iostream>
#include<vector>
using namespace std;

typedef long long LL;
const int N = 300010, P = 1e9+7;

vector<int> g[N];
int sz[N],tr[N];
int n,root;
int sum=1,s1,s2;

void add(int x,int y)
{
	for(int i=x;i<N;i+=(i&-i))
		tr[i]+=y;
}

int query(int x)
{
	int res=0;
	for(int i=x;i;i-=(i&-i))
		res+=tr[i];
	return res;
}

void dfs(int u,int fa)
{
	add(u,1);
	s1=(s1+query(n)-query(u))%P;
	
	sz[u]=1;
	int cnt=0;
	for(auto &j:g[u])
	{
		if(j==fa) continue;
		dfs(j,u);
		sz[u]+=sz[j];
		cnt++;
	} 
	
	for(int i=1;i<=cnt;i++)
		sum=(LL)sum*i%P;
	
	s2=(s2+n-query(n)-sz[u]+1)%P;
	add(u,-1);	
}

int main()
{
	scanf("%d%d",&n,&root);
	for(int i=0;i<n-1;i++)
	{
		int a,b;scanf("%d%d",&a,&b);
        g[a].push_back(b);
        g[b].push_back(a); 
	}
	
	dfs(root,-1);
	
	int ans=((LL)s1*sum+(LL)s2*sum%P*(P+1)/4)%P;
	printf("%d\n",ans);
	return 0; 
}
解决编程中的“最大逆序对”通常涉及查找数组中两个元素,使得前一个元素大于后一个元素,并且它们之间的差值最大。这在排序算法中是一个经典问,可以使用哈希表或者双指针法来寻找解决方案。 一种常见思路是使用两趟遍历: 1. 第一趟遍历数组,计算每个元素的右边界,即从该位置到数组结尾的最大元素下标。将结果存储在一个新的数组或哈希表中。 2. 第二趟遍历数组,对于每个元素,找到其左边界(初始值为0),然后在哈希表中查找比它大的元素的右边界。两个元素的下标之差就是逆序对,更新最大逆序对的记录。 Python伪代码示例: ```python def maxInversionPair(arr): n = len(arr) # 计算每个元素右边界 right_boundaries = [-1] * n for i in range(1, n): right_boundaries[i] = find_max_right(arr, i, n) # 查找最大逆序对 max_inversion = (0, 0), 0 for i in range(n): if right_boundaries[i] != -1: max_inversion = max(max_inversion, (i, right_boundaries[i]), key=lambda x: x[1] - x[0]) return max_inversion def find_max_right(arr, start, end): max_val = arr[start] max_right = start for i in range(start + 1, end + 1): if arr[i] > max_val: max_val = arr[i] max_right = i return max_right # 示例数组 arr = [2, 4, 1, 3, 5] max_inversion, inversion_value = maxInversionPair(arr) print(f"最大逆序对是 ({arr[max_inversion[0]]}, {arr[max_inversion[1]]}),逆序值为 {inversion_value}")
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wa_Automata

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值