2025牛客多校第九场 G.排列 A.AVL树 F.军训 个人题解
F.军训
数学 #曼哈顿距离
题目

思路
首先很容易想到的是,一定可以通过旋转到达目标状态,不会有-1的情况
接下来是一个关键的观察:关注双脚所在中点的移动

发现实际上中点移动一个单位曼哈顿距离就代表一次旋转
因此进行坐标变换即可
代码实现
#include<iostream>
#include<vector>
#include<map>
#include<cmath>
#include<set>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
constexpr ll inf = 1e9 + 5;
#define int ll
#define double long double
void trans(double&x,double&y){
double u=x+y,v=x-y;
x=u,y=v;
}
void eachT() {
int sx1,sy1,sx2,sy2,tx1,ty1,tx2,ty2;
cin>>sx1>>sy1>>sx2>>sy2>>tx1>>ty1>>tx2>>ty2;
double sx=1.0*(sx1+sx2)/2,sy=1.0*(sy1+sy2)/2;
double tx=1.0*(tx1+tx2)/2,ty=1.0*(ty1+ty2)/2;
trans(sx,sy),trans(tx,ty);
double dis=abs(sx-tx)+abs(sy-ty);
cout<<(int)dis<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll t = 1;
cin >> t;
while (t--) {
eachT();
}
}
A.AVL树
树上dp #dp #dfs
题目

思路
本题是一个比较巧妙的树上dp,状态设置较为特殊
状态含义:
\(dp[u][h]\)表示将节点\(u\)及其子树修改为高度为\(h\)的\(AVL\)树所需要的最少操作次数
状态转移:
&dp[u][h]=min\{ dp[u][h],dp[lson][h-1]+dp[rson][h-1] \}\\ \\
&dp[u][h]=min\{ dp[u][h],dp[lson][h-2]+dp[rson][h-1] \}\\ \\
&dp[u][h]=min\{ dp[u][h],dp[lson][h-1]+dp[rson][h-2] \}
\end{align}
\]
由于两棵子树的高度差不能超过1,所以枚举左右子树的三种情况进行状态转移
注意到转移最开始的时候需要调用\(dp[u][0]\)的值,其含义为:将\(u\)及其子树完全删除所需要的最少操作次数,因此该值即为\(u\)的子树大小\(size\),跑一遍\(dfs\)预处理即可
最麻烦的事情是\(dp[0][h]\),若\(u\)没有左儿子,那么其\(lson\)的值即为\(0\),此时dp需要访问\(dp[0][h]\)的值,代表构建一棵高度为\(h\)的\(AVL\)树所需要的最小操作次数

画出高度为\(2,3,4,5\)的最简\(AVL\)树,会发现子树之间存在特别的递推关系:
当前子树必定等于其爷爷节点的另一个子树,
当前子树大小等于孙子子树大小加上另一个儿子子树大小再加上自己本身
即:
\]
至此可以完成树上dp的全过程
代码实现
#include<iostream>
#include<vector>
#include<unordered_map>
#include<cmath>
#include<string>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
constexpr ll inf = 1e9 + 5;
const int N=2e5+5,H=40;
int dp[N][H],n;
void init(){
dp[0][1]=1,dp[0][2]=2;
rep(i,3,H-1){
dp[0][i]=dp[0][i-1]+dp[0][i-2]+1;
}
}
struct node{
int ls,rs,siz;
}a[N];
int dfs(int u){
if(u==0)return 0;
a[u].siz=1;
a[u].siz+=dfs(a[u].ls);
a[u].siz+=dfs(a[u].rs);
dp[u][0]=a[u].siz;
rep(i,1,H-1)dp[u][i]=inf;
return a[u].siz;
}
void dfs2(int u){
if(u==0)return;
dfs2(a[u].ls);
dfs2(a[u].rs);
int ls=a[u].ls,rs=a[u].rs;
rep(j,1,H-1){
dp[u][j]=min(dp[u][j],dp[ls][j-1]+dp[rs][j-1]);
if(j-2>=0)dp[u][j]=min(dp[u][j],dp[ls][j-2]+dp[rs][j-1]);
if(j-2>=0)dp[u][j]=min(dp[u][j],dp[ls][j-1]+dp[rs][j-2]);
}
}
void eachT() {
cin>>n;
rep(i,1,n){
cin>>a[i].ls>>a[i].rs;
}
dfs(1);
dfs2(1);
int ans=inf;
rep(j,0,H-1){
ans=min(ans,dp[1][j]);
}
cout<<ans<<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll t = 1;
init();
cin >> t;
while (t--) {
eachT();
}
}
G.排列
组合数 #笛卡尔树 #dfs
题目

思路
考虑一组样例:
\]
操作分为删去与打印两种,若想要打印数字\(i\),则\(i\)必须为当前序列的最小值
因此发现打印的可行性具有一定的顺序,比如我想打印\(4\),那么必然要先将\(1,2,3\)删去
而删去的过程中又必然会将某些经过的点提前删去,因此尝试画出路径图:

发现这实际上就是一棵笛卡尔树!
- 树中节点的左右关系与原序列中的一致
- 树中的父亲节点必定比左右儿子要小,对应着必须先走过小的节点才能走到大的节点
接下来就是如何“数数”了:
我们可以通过打印序列的最后一个元素是谁,来进行分类
这样分类的依据就在于,每次打印的元素必然是序列中的最小元素,因此枚举每个数作为最后一个打印的数,不会出现情况间的重复与遗漏

以数字\(4\)为例,想要走到四号节点,必然需要先删去某些节点
- 比4大的1、3必然要删除
- 原序列中,在3左边、1右边的所有数都要删除,这样才能删掉1、3
综上 ,发现保留下来的数正好就是4所在的子树(蓝色区域)
由于删除操作已经占了\(n-size[u]\)次,那么能够打印序列的最大长度正是\(size[u]\)
若把左边看作最后一次打印的元素,右边看作第一次打印的元素,那么会发现打印序列是一个单调不增序列,并且开头元素固定为4
开头固定的单调不减序列有多少种方案数?
自然是使用占位置的隔板法进行统计:
- 能够出现在打印序列中的数字必然是比4小且靠中间的位置,即沿着4往根部走,途中遇到的所有数字
- 能够打印的数字的数量即为4的深度-1,\(dep-1\),即1、3两个数字
- 因此有\(l_{4},l_{3},l_{1}\)三块隔板,\(l_{i}\sim l_{i-1}\)间填数字\(i-1\)即可构造单调不增序列
- \(l_{1}\)的右边没有东西,填0代表空着,这也对应了长度小于\(size[u]\)的情况
因此方案数计算公式为:
\]
在dfs回溯过程中,\(size[u],dep[u]\)均已知,可以直接算答案
因此一遍dfs即可算出答案
代码实现
#include<iostream>
#include<vector>
#include<unordered_map>
#include<cmath>
#include<stack>
using namespace std;
using ll = long long;
#define rep(i, a, b) for(ll i = (a); i <= (b); i ++)
#define per(i, a, b) for(ll i = (a); i >= (b); i --)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
constexpr ll inf = 1e9 + 5;
#define int ll
const int N=1e6+5,mod=998244353;
int n;
int qpow(int a,int b){
a%=mod;int res=1;
while(b){
if(b%2){res*=a;res%=mod;}
a*=a;a%=mod;b>>=1;
}
return res%mod;
}
vector<int>A,inv;
void inv0(int len){
A.resize(len+1),inv.resize(len+1);
A[0]=1;inv[0]=1;
rep(i,1,len){
A[i]=(A[i-1]*i)%mod;
inv[i]=qpow(A[i],mod-2);
}
}
int C(int n,int m){
if(m>n)return 0;
return A[n]*inv[m]%mod*inv[n-m]%mod;
}
struct node{
int ls,rs;
}a[N];
int ans;
int dfs(int u,int dep){
if(u==0)return 0;
int siz=1;
int ls=a[u].ls,rs=a[u].rs;
siz+=dfs(ls,dep+1)+dfs(rs,dep+1);
ans+=C(siz+dep-1,dep);
ans%=mod;
return siz;
}
void eachT() {
rep(i,1,n){
a[i].ls=a[i].rs=0;
}
cin>>n;
stack<int>st;
rep(i,1,n){
int x;cin>>x;
if(st.empty())st.push(x);
else{
if(x<st.top()){
int son=0;
while(!st.empty()&&x<st.top()){
son=st.top();st.pop();
}
a[x].ls=son;
if(!st.empty())a[st.top()].rs=x;
st.push(x);
}else{
a[st.top()].rs=x;
st.push(x);
}
}
}
ans=0;
dfs(1,1);
cout<<(ans+1)%mod<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
inv0(5e6+5);
ll t = 1;
cin >> t;
while (t--) {
eachT();
}
}
2025牛客多校第九场 G.排列 A.AVL树 F.军训 个人题解的更多相关文章
- Cutting Bamboos(2019年牛客多校第九场H题+二分+主席树)
题目链接 传送门 题意 有\(n\)棵竹子,然后有\(q\)次操作,每次操作给你\(l,r,x,y\),表示对\([l,r]\)区间的竹子砍\(y\)次,每次砍伐的长度和相等(自己定砍伐的高度\(le ...
- 牛客多校第九场 && ZOJ3774 The power of Fibonacci(二次剩余定理+斐波那契数列通项/循环节)题解
题意1.1: 求\(\sum_{i=1}^n Fib^m\mod 1e9+9\),\(n\in[1, 1e9], m\in[1, 1e4]\) 思路1.1 我们首先需要知道斐波那契数列的通项是:\(F ...
- [2019牛客多校第二场][G. Polygons]
题目链接:https://ac.nowcoder.com/acm/contest/882/G 题目大意:有\(n\)条直线将平面分成若干个区域,要求处理\(m\)次询问:求第\(q\)大的区域面积.保 ...
- 牛客多校第二场 G transform
链接:https://www.nowcoder.com/acm/contest/140/G White Cloud placed n containers in sequence on a axes. ...
- 2019牛客多校第九场AThe power of Fibonacci——扩展BM
题意 求斐波那契数列m次方的前n项和,模数为 $1e9$. 分析 线性递推乘线性递推仍是线性递推,所以上BM. 由于模数非质数,上扩展版的BM. 递推多少项呢?本地输入发现最大为与前57项有关(而且好 ...
- 牛客多校第九场 A The power of Fibonacci 杜教bm解线性递推
题意:计算斐波那契数列前n项和的m次方模1e9 题解: $F[i] – F[i-1] – F[i-2] = 0$ $F[i]^2 – 2 F[i-1]^2 – 2 F[i-2]^2 + F[i-3] ...
- 2018牛客多校第九场E(动态规划,思维,取模)
#include<bits/stdc++.h>using namespace std;const long long mod=1000000007,inv=570000004;long l ...
- 牛客多校第九场 J Symmetrical Painting 计算几何/扫描线
题意: 平面上有几个宽度相同的矩形区域被涂黑了,让你找到一条横线横截若干个矩形,把这些黑色部分抠下来一部分使得它们以这条横线为对称轴,求能抠下来的最大面积. 题解: 在随着对称轴上移的过程中,必然有一 ...
- 牛客多校第九场 E All men are brothers 并查集/组合论
题意: 一开始有n人互不认识,每回合有两个人认识,认识具有传递性,也就是相互认识的人组成小团体.现在问你每个回合,挑选四个人,这四个人互不认识,有多少种挑选方法. 题解: 认识不认识用并查集维护即可, ...
- 牛客多校第九场 D Knapsack Cryptosystem 背包
题意: 给你32个物品,给定一个容积,让你恰好把这个背包装满,求出装满的方案 题解: 暴力计算的话,复杂度$2^{32}$肯定会炸,考虑一种类似bsgs的算法,先用$2^{16}$的时间遍历前一半物品 ...
随机推荐
- odoo18运行报错问题解决
File "/Users/melon/.pyenv/versions/3.11.9/lib/python3.11/code.py", line 90, in runcode exe ...
- 关于家庭宽带IPv6的开启、绑定域名、使用教程等
前言 好几年前网上就很多讨论家庭宽带支持IPv6,能怎么怎么改变生活.带来多少便利,奈何之前租的房子是房东办好的宽带,我没有权限接触到光猫,自己也一直懒得研究这个新鲜事物,前阵子自己捣鼓了一下,还是得 ...
- 阿里也出手了!灵码AI IDE问世
大家好,我是晓凡. 写在前面 各位程序员小伙伴们,是不是还在为写代码头秃?别担心,阿里云带着它的通义灵码 AI IDE 来拯救你啦! 相信不少小伙伴已经在VSCode.JetBrains IDE等主流 ...
- DeepSeek-V3
一.与DeepSeek-v2比较 1. 架构和参数(Architecture and Parameters) DeepSeek-V3采用 Mixture-of-Experts (MoE) 架构,共有6 ...
- FastAPI访问令牌的权限声明与作用域管理:你的API安全真的无懈可击吗?
title: FastAPI访问令牌的权限声明与作用域管理:你的API安全真的无懈可击吗? date: 2025/06/15 06:32:07 updated: 2025/06/15 06:32:07 ...
- C#实现语音预处理:降噪、静音检测、自动增益(附Demo源码)
无论是在音视频录制系统,还是音视频通话系统.或视频会议系统中,对从麦克风采集到的说话的声音数据进行预处理,都是是非常必要的. 语音数据预处理主要包括:降噪(Noise Reduction).静音检 ...
- Vertx 实现webapi实战项目(五)
添加测试handler 一:定义上传json,注意,mId是必须的. 1 { 2 "mId": 101, 3 "name":"cddd", ...
- C# 通过反射 Model 转 XML
Order.OrderRequest m = new Order.OrderRequest(); m.Request = new Order.OrderRequest.Reque ...
- ORACLE--SQL日常问题和技巧1(列变行、自定义查询结构、将字符串分割为多条记录)
1.ORACLE列变行加合计: 查询结果为 1 SELECT 2 nvl( F, '合计' ) F, 3 sum( S1 ) S1, 4 sum( S2 ) S2, 5 sum( S3 ) S3 6 ...
- centos stream 9 安装 java
# 下载java并解压到任意目录,比如我这里是 /home/soft cd /home/soft wget https://download.oracle.com/java/24/latest/jdk ...