zoukankan      html  css  js  c++  java
  • 「杂烩」精灵魔法(P1908逆序对弱化版)

    「杂烩」精灵魔法(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)

    这就不用写了吧,归并能处理逆序对(貌似也只有这个用途)

  • 相关阅读:
    《Java大学教程》—第12章 案例研究--第2部分
    《Java大学教程》—第11章 案例研究--第1部分
    《Java大学教程》—第10章 图形和事件驱动程序
    《Java大学教程》—第8章 通过继承扩展类
    《Java大学教程》—第7章 类的实现
    《Java大学教程》—第6章 类和对象
    《Java大学教程》—第5章 数组
    《Java大学教程》—第4章 方法的实现
    spring_01概念及案例
    MyEclipse中jsp编码设置
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/13256575.html
Copyright © 2011-2022 走看看