1. 程式人生 > 實用技巧 >Flex佈局語法

Flex佈局語法

「雜燴」精靈魔法(P1908逆序對弱化版)

題面:

題目描述

\(Tristan\)解決了英靈殿的守衛安排後,便到達了靜謐的精靈領地——\(Alfheim\) 。由於$ Midgard$ 處在$ Alfheim\(和冥界\) Hel$ 的中間,精靈族領地尚未受到冥界惡靈的侵入。族長$ Galanodel \(為了幫助米德加爾特抵禦外敵,對邪惡亡靈軍團使用了高等魔法,從而使得亡靈軍團每個士兵的行進速度變得不一致,從而打亂冥王\)Hel$安排的最佳陣型。

由於這個軍團離\(Midgard\)還很遠,因此在抵達$ Midgard$ 之前,對於$ A,B$ 兩個亡靈,若$A \(的初始位置在\) B\(後面且\)A \(的速度比 快,\)A \(就會衝到\)B \(的前面去。現在\)Galanodel$ 想知道,會有多少對亡靈之間出現反超現象?

輸入格式

第一行一個整數$ n$,表示排成一隊的邪惡亡靈軍團有多少人。

第二行\(n\)個整數\(a[i]\),表示邪惡亡靈們在數軸上的初始座標。資料保證這些座標全部不同。亡靈軍團向數軸正方向前進。

第三行\(n\)個整數 \(v[i]\),表示邪惡亡靈們的行進速度。

輸出格式

一行一個正整數\(k\),表示「反超」的個數。

樣例

樣例輸入

3

1 2 3

2 1 3

樣例輸出

1

資料範圍與提示

$ n<= 100000$

所有資料的絕對值均不超過\(maxlongint\)。

解法一:樸素的暴力(50pts)

一看就讓我們求排序後的逆序對,\(O(n^2)\)搞它

程式碼:



/*#!/bin/sh
dir=$GEDIT_CURRENT_DOCUMENT_DIR
name=$GEDIT_CURRENT_DOCUMENT_NAME
pre=${name%.*}
g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
if test $? -eq 0; then
gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
fi*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
typedef long long ll;
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n;
ll ans,minn,maxx;
struct Node{
ll d,v;
}a[maxn];
bool cmp(Node A,Node B){return A.d<B.d;}
int main(){
//freopen("a.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)cin>>a[i].d;
for(int i=1;i<=n;i++)cin>>a[i].v;
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(a[i].v>a[j].v)ans++;
}
}
cout<<ans;
return 0;
}

解法2:智障的set(40pts)

先介紹個函式:\(algorithm\)庫裡的\(distance\)函式能夠輕易的幫我們把迭代器轉成下標處理,用到set上就十分合適

利用set自動排序的功能,可以輕易處理逆序對,將位置從大到小\(sort\)一遍,找新序列的正序對就行了(正序是逆序對,倒序不就是正序對麼)

如果新元素比隊尾元素大,說明前面所有元素都能於它構成正序對,相反,就可以利用\(lowerbound\)函式找到第一個大於等於它的位置,恰好是它能組成的正序對個數

但是由於set時間效率極低,還沒暴力跑的快,用這個練習練習STL就行了

程式碼:



/*#!/bin/sh
dir=$GEDIT_CURRENT_DOCUMENT_DIR
name=$GEDIT_CURRENT_DOCUMENT_NAME
pre=${name%.*}
g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
if test $? -eq 0; then
gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
fi*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<set>
typedef long long ll;
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,f[maxn];
ll ans;
struct Node{
ll d,v;
}a[maxn];
multiset<int> s;
bool cmp(Node A,Node B){return A.d>B.d;}
int main(){
// freopen("a.in","r",stdin);
n=read();
for(int i=0;i<n;i++)cin>>a[i].d;
for(int i=0;i<n;i++)cin>>a[i].v;
sort(a,a+n,cmp);
s.insert(a[0].v);
f[0]=0;
for(int i=1;i<n;i++){
int len=s.size();
if(a[i].v>=*s.rbegin()){//注意,set的end()是假的,只能這樣求隊尾元素
f[i]=len,s.insert(a[i].v);
}else{
// cout<<(lower_bound(ve.begin(),ve.end(),a[i].v),a[i].v)<<endl;
f[i]=distance(s.begin(),s.lower_bound(a[i].v));//distance將迭代器轉下標
s.insert(a[i].v);
}
//for(int i=0;i<n;i++)cout<<ve[i]<<" ";
//cout<<endl;
}
for(int i=0;i<n;i++)ans+=f[i];
cout<<ans;
return 0;
}

解法3:vector模擬set(100pts)

眾所周知,$$vector功能強大,輕易就能排個序,效率比set還高不少,但是終究過不了加強版的P1908

程式碼:



/*#!/bin/sh
dir=$GEDIT_CURRENT_DOCUMENT_DIR
name=$GEDIT_CURRENT_DOCUMENT_NAME
pre=${name%.*}
g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
if test $? -eq 0; then
gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
fi*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
typedef long long ll;
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,f[maxn];
ll ans;
struct Node{
ll d,v;
}a[maxn];
vector<int> ve;
bool cmp(Node A,Node B){return A.d>B.d;}
int main(){
// freopen("a.in","r",stdin);
n=read();
for(int i=0;i<n;i++)cin>>a[i].d;
for(int i=0;i<n;i++)cin>>a[i].v;//狗比vector只能從0開始存
sort(a,a+n,cmp);
ve.push_back(a[0].v);
f[0]=0;
for(int i=1;i<n;i++){
int len=ve.size();
if(a[i].v>=ve.back()){
f[i]=len,ve.push_back(a[i].v);
}else{
// cout<<(lower_bound(ve.begin(),ve.end(),a[i].v),a[i].v)<<endl;
f[i]=lower_bound(ve.begin(),ve.end(),a[i].v)-ve.begin();//注意寫法,考試就在這被卡住了
ve.insert(lower_bound(ve.begin(),ve.end(),a[i].v),a[i].v);//假裝排序
}
//for(int i=0;i<n;i++)cout<<ve[i]<<" ";
//cout<<endl;
}
for(int i=0;i<n;i++)ans+=f[i];
cout<<ans;
return 0;
}

解法4:樹狀陣列(100pts)

對結構體\(a[i]\)排序,按位置從小到大的順序排,利用輔助陣列b記錄排序後的位置,將位置插入樹狀陣列,諮詢一下字尾和即可

程式碼:



/*#!/bin/sh
dir=$GEDIT_CURRENT_DOCUMENT_DIR
name=$GEDIT_CURRENT_DOCUMENT_NAME
pre=${name%.*}
g++ -O2 $dir/$name -o $pre -g -Wall -std=c++11
if test $? -eq 0; then
gnome-terminal -x bash -c "time $dir/$pre;echo;read;"
fi*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<set>
typedef long long ll;
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,f[maxn];
ll ans,b[maxn],c[maxn];
struct Node{
ll d,v;
}a[maxn];
bool cmp(Node A,Node B){return A.d<B.d;}
ll Lowbit(ll x){return x&-x;}
void update(ll x){
while(x<=n){
c[x]++;
x+=Lowbit(x);
}
}
ll query(ll x){
ll sum=0;
while(x){
sum+=c[x];
x-=Lowbit(x);
}
return sum;
}
int main(){
//freopen("a.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)cin>>a[i].d;
for(int i=1;i<=n;i++)cin>>a[i].v,b[i]=a[i].v;
sort(a+1,a+n+1,cmp);
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)a[i].v=lower_bound(b+1,b+1+n,a[i].v)-b;
for(int i=n;i>=1;i--){
ans+=query(a[i].v-1);update(a[i].v);
}
cout<<ans;
return 0;
}

解法5:歸併排序(100pts)

這就不用寫了吧,歸併能處理逆序對(貌似也只有這個用途)