当前位置: 首页 > ops >正文

2025杭电多校赛(2)1006 半

题目整理

问题描述
某学校举办两场个人比赛,共有相同的 n 名选手(编号 1∼n)。已知两场比赛的排名结果:

  • 第一场排名:a1​,a2​,⋯,an​(从高到低)
  • 第二场排名:b1​,b2​,⋯,bn​(从高到低)

其中 a 和 b 均为 1∼n 的排列(无重复排名)。
作为选手 i,你可以通过禁赛其他选手,使得自己在两场比赛中均成为第一名。要求计算:对于每个选手 i,最少需要禁赛多少名选手
(禁赛后,剩余选手的相对排名保持原顺序不变)

输入格式

  • 第一行:整数 T(1≤T≤20),表示测试数据组数
  • 每组数据:
    • 第一行:整数 n(1≤n≤106)
    • 第二行:n 个整数 a1​,a2​,⋯,an​(第一场排名)
    • 第三行:n 个整数 b1​,b2​,⋯,bn​(第二场排名)
  • 总数据规模:所有测试数据的 n 之和不超过 2×106

输出格式

  • 每组测试数据输出一行:n 个整数,第 i 个整数表示选手 i 的最少禁赛人数

Sample Input

1

5

2 1 5 4 3

2 4 5 1 3

Sample Output

3 0 4 3 3

这题题意不难,就是找到一个数在两个数组中前面的数的集合的交集大小,而在这里我们要好好利用这两个数组是n的全排列的性质。

注意下面的代码是超时的,优化方法后面说。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,temp,a[1000005],b[1000005],n,ans[1000005];
set<int>arr;
int main() {
scanf("%d",&t);
while(t--){arr.clear();scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=1;i<=n;i++){scanf("%d",&temp);b[temp]=i;}for(int i=1;i<=n;i++){arr.insert(b[a[i]]);int s1=i-distance(arr.begin(),arr.find(b[a[i]]))-1;int s2=b[a[i]]-1;ans[a[i]]=s1+s2; }for(int i=1;i<=n;i++){printf("%d ",ans[i]);}printf("\n");
}return 0;
}

解释:这里的a数组就是和题目的一样,而b数组记录的是数字为i的下标,这是为了利用全排列的性质。

1.我们从前向后遍历a数组,那么i-1就是这个数(now)前面有几个数。我们现在要确定这些数有几个在b数组的now的前面,也就是重复的。

2.我一开始用set来实现,每次就把遍历到的数now的在b数组的下标插入到set中,因为set会自动排序,而且下标是不会重复的。这样我们每次查询前面有几个数就是有几个重复的(我这样表述的不好),下面我讲线段树维护可能好懂一些。但是distance函数速度是n,我本以为是常数,这样就是n^2算法,和暴力没什么区别。

3.那只有用树状数组或线段树维护。我们要维护的实际是一个前缀和数组,而这个数组每次都要有一个数从0变为1,每次我们也都要查询一个特定的前缀和值。那这就是线段树的单点修改,区间查询。套用线段树模板就行。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{long long r,l,sum,lz;
};
node tree[5000005];
int t,temp,a[1000005],b[1000005],n,ans[1000005];
void init(){for(int i=1;i<=5000000;i++){tree[i].l=tree[i].lz=tree[i].r=tree[i].sum=0;}
}
void push_down(int i)//下传函数
{if(tree[i].lz!=0){tree[i*2].lz+=tree[i].lz;tree[i*2+1].lz+=tree[i].lz;tree[i*2].sum+=tree[i].lz*(tree[i*2].r-tree[i*2].l+1);tree[i*2+1].sum+=tree[i].lz*(tree[i*2+1].r-tree[i*2+1].l+1);tree[i].lz=0;}return;
}
void build(int i,int l,int r)//建树
{tree[i].l=l,tree[i].r=r;tree[i].lz=0;if(l==r){return;}int mid=(l+r)/2;build(2*i,l,mid);build(2*i+1,mid+1,r);
}
void add(int i,int l,int r,int k)//区间加减
{if(tree[i].l>=l&&tree[i].r<=r){tree[i].sum+=k*(tree[i].r-tree[i].l+1);tree[i].lz+=k;return;}push_down(i);if(tree[i*2].r>=l){add(i*2,l,r,k);}if(tree[i*2+1].l<=r){add(i*2+1,l,r,k);}tree[i].sum=tree[2*i].sum+tree[2*i+1].sum;return;
}
long long search(int i,int l,int r)//查询
{if(tree[i].l>=l&&tree[i].r<=r){return tree[i].sum;}if(tree[i].r<l||tree[i].l>r){return 0;}push_down(i);long long ans=0;if(tree[i*2].r>=l){ans+=search(i*2,l,r);}if(tree[i*2+1].l<=r){ans+=search(i*2+1,l,r);}return ans;
}
int main() {
scanf("%d",&t);
while(t--){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=1;i<=n;i++){scanf("%d",&temp);b[temp]=i;}build(1,1,n);for(int i=1;i<=n;i++){add(1,b[a[i]],b[a[i]],1);ans[a[i]]=b[a[i]]-1+search(1,b[a[i]],n)-1;}for(int i=1;i<=n;i++){printf("%d ",ans[i]);}init();printf("\n");
}return 0;
}

http://www.xdnf.cn/news/15936.html

相关文章:

  • I2S音频的时钟
  • Zabbix 企业级分布式监控系统深度解析
  • Leetcode力扣解题记录--第238题(前/后缀积)
  • Windows防火墙配置详解
  • 暑期算法训练.5
  • Xilinx FPGA XCKU115‑2FLVA1517I AMD KintexUltraScale
  • day058-docker常见面试题与初识zabbix
  • 果园里的温柔之手:Deepoc具身智能如何重塑采摘机器人的“生命感知”
  • CS课程项目设计4:支持AI人机对战的五子棋游戏
  • 计算机网络中:传输层和网络层之间是如何配合的
  • buntu 22.04 上离线安装Docker 25.0.5(二)
  • 动静态库原理与实战详解
  • Pycaita二次开发基础代码解析:边线提取、路径追踪与曲线固定
  • WebAPIs事件流与事件委托与其他事件
  • 力扣15:三数之和
  • 识别PDF中的二维码
  • Android开发中卡顿治理方案
  • 通俗易懂卷积神经网络(CNN)指南
  • 【PTA数据结构 | C语言版】双连通分量
  • 【Spark征服之路-3.6-Spark-SQL核心编程(五)】
  • 处理excel/wps表格中数值格式的警告的工具和脚本
  • SQL审计、Archery实战记录
  • 代码随想录算法训练营第二十七天
  • 算法训练营DAY37 第九章 动态规划 part05
  • channel_up和lane_up
  • Promise 详解与实现:从原理到实践
  • 【设计模式C#】工厂方法模式(相比简单工厂模式更加具有灵活性和扩展性的工厂模式)
  • Day07_网络编程20250721(网络编程考试试卷)
  • 本地部署Dify、Docker重装
  • JAVA后端开发—— JWT(JSON Web Token)实践