精灵魔法(vector逆序对,离散化数状数组)
精灵魔法
题目大意
\(Tristan\) 解决了英灵殿的守卫安排后,便到达了静谧的精灵领地—— \(Alfheim\)。由于 \(Midgard\) 处在 \(Alfheim\) 和冥界 \(Hel\) 的中间,精灵族领地尚未受到冥界恶灵的侵入。族长 \(Galanodel\) 为了帮助米德加尔特抵御外敌,对邪恶亡灵军团使用了高等魔法,从而使得亡灵军团每个士兵的行进速度变得不一致,从而打乱冥王 \(Hel\) 安排的最佳阵型。
由于这个军团离 \(Midgard\) 还很远,因此在抵达 \(Midgard\) 之前,对于 \(A,B\) 两个亡灵,若 \(A\) 的初始位置在 \(B\) 后面且 \(A\) 的速度比 \(B\) 快,\(A\) 就会冲到 \(B\) 的前面去。现在 \(Galanodel\) 想知道,会有多少对亡灵之间出现反超现象?
输入格式和输出格式
\(input\)
第一行一个整数 \(n\),表示排成一队的邪恶亡灵军团有多少人。
第二行 \(n\) 个整数 \(a[i]\),表示邪恶亡灵们在数轴上的初始坐标。数据保证这些坐标全部不同。亡灵军团向数轴正方向前进。
第三行 \(n\) 个整数 \(v[i]\),表示邪恶亡灵们的行进速度。
\(output\)
一行一个正整数 \(k\),表示「反超」的个数。
样例
\(in\)
3
1 2 3
2 1 3
\(out\)
1
数据范围
对于 \(30%\) 的数据,\(1<=N<=1000\);
对于\(100%\) 的数据,\(1<=N<=105\)。
所有数据的绝对值均不超过 \(maxlongint\)。
主要思路
本题是道签到题,但是(我并没有拿到满分)却延伸出来很多思路,首先就是个逆序对,可以用归并排序实现,也可以懒一点用vector来实现,反超的两个前提是位置靠后,速度比位置考前的快,那么只需要先用sort以位置排序,然后用vector来求在其前面的长度(时间效率可能会低,懒人专用),实际上和归并都是利用了一个逆序对的思想,其次就是数状数组,不多解释了,基操嘛~~(没写过数状数组,只会线段树)~~,直接上代码了qwq。
代码实现
vector实现
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int n,f[maxn];
long long ans;
vector<int> v;
struct Node{int a,v;}node[maxn];
bool cmp(Node A, Node B){return A.a>B.a;}
int main(){
scanf("%d",&n);
for(int i = 0; i < n; i++){
scanf("%d",&node[i].a);
}
for(int i = 0; i < n; i++){
scanf("%d",&node[i].v);
}
sort(node, node + n, cmp);
v.push_back(node[0].v);
f[0] = 0;
for(int i = 1; i < n; i++){
int len = v.size();
if(node[i].v>v.back()){
v.push_back(node[i].v);f[i] = len;
}else{
f[i] = lower_bound(v.begin(),v.end(),node[i].v) - v.begin();
v.insert(lower_bound(v.begin(),v.end(),node[i].v),node[i].v);
}
}
for(int i = 0; i < n; i++){
ans += f[i];
}
printf("%lld",ans);
}
归并排序
#include <bits/stdc++.h>
const int maxn=1e5+5,Inf=2147483647;
typedef long long LL;
struct Node{
LL pos,v;
bool operator <(const Node &a)const{
return pos<a.pos;
}
}e[maxn];
LL a[maxn],b[maxn],ans=0;
int n;
void Read(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&e[i].pos);
for(int i=1;i<=n;++i)
scanf("%lld",&e[i].v);
std::sort(e+1,e+n+1);
for(int i=1;i<=n;++i)
a[i]=e[i].v;
}
void Print(){
for(int i=1;i<=n;++i)
printf("%lld ",a[i]);
}
void Merge(int l,int mid,int r){//合并操作
int i=l,j=mid+1,k=0;//i指向前面区间第一个元素,j指向后面区间第一元素
while(i<=mid && j<=r){//取两个序列前面的较小者
if(a[i]<=a[j])b[++k]=a[i++];
else{
ans+=mid-i+1;//a[i]~a[mid]均能和a[j]组成逆序对
b[++k]=a[j++];
}
}//跳出循环两个序列中有一个为空
while(i<=mid)//若比较完之后,第一个有序区仍有剩余
b[++k]=a[i++];
while(j<=r)//若比较完之后,第二个有序区仍有剩余
b[++k]=a[j++];
for(i=l,k=1;i<=r;++i,++k)//把合并后的排好序的序列拷贝到数组a[l,r]
a[i]=b[k];
}
void Merge_sort(int l,int r){
if(l<r){//把区间分成两部分
int mid=l+(r-l)/2;
Merge_sort(l,mid);//递归左区间
Merge_sort(mid+1,r);//递归右区间
Merge(l,mid,r);//合并左右两区间
}
}
void Solve(){
Read();
Merge_sort(1,n);
printf("%lld\n",ans);
}
int main(){
Solve();
return 0;
}
数状数组
#include <bits/stdc++.h>
typedef long long LL;
const LL maxn=1e5+5;
struct Node{
LL pos,v;
bool operator <(const Node &a)const{
return pos<a.pos;
}
}a[maxn];
LL b[maxn],c[maxn];
LL n;
LL lowbit(LL x){
return x & -x;
}
void Updata(LL x){
for(LL i=x;i<=n;i+=lowbit(i))
c[i]++;
}
LL Query(LL x){
LL ans=0;
for(LL i=x;i;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
void Solve(){
scanf("%lld",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i].pos);
for(int i=1;i<=n;++i){
scanf("%lld",&a[i].v);
b[i]=a[i].v;
}
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i=1;i<=n;++i)
a[i].v=lower_bound(b+1,b+n+1,a[i].v)-b;
LL ans=0;
for(int i=n;i>=1;--i){
ans+=Query(a[i].v-1);
Updata(a[i].v);
}
printf("%lld\n",ans);
}
int main(){
Solve();
return 0;
}
谢谢观看,点个关注qwq