The 2018 JUST Collegiate Programming Contest C题 求逆序对

本文介绍了一种计算逆序对数量的方法,通过使用归并排序和树状数组两种算法实现。归并排序通过在排序过程中计数逆序对,而树状数组则在查询比当前元素小的已出现元素数量时更新逆序对计数。

大致题意:给了两行数,相同的数相连,求有多少对相交线

思路:数组a[i]存第二行第i个数在第一行的下标,然后求逆序对数就好了。

以前不会求逆序对数,网上看了两种方法,一种是树状数组,一种类似递归的归并排序,复杂度都是O(nlogn)。

1.归并排序写法:C - Intersections -逆序数-归并排序

只要需要交换,则逆序对数+1,然后交换。(排序结果从小到大)

当前已经排好序的左右两段各自内部的逆序对数已经加在ans里面了,且左右两段已经有序,所以在进行下一层归并的时候,只需要当取右段中的元素进行归并时ans++。


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 1008611
ll ans,n,x,t;
int a[maxn],rp[maxn];
void msort(int l,int r)
{
    if(l>=r)
        return ;
    int mid=(l+r)/2;
    msort(l,mid);
    msort(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j])//正序
            rp[k++]=a[i++];
        else//逆序
        {
            rp[k++]=a[j++];
            ans+=mid-i+1;
        }
    }
    //归并
    while(i<=mid)
        rp[k++]=a[i++];
    while(j<=r)
        rp[k++]=a[j++];
    //归并结果放回a中
    for(int i=l; i<=r; i++)
        a[i]=rp[i];
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        ans=0;
        cin>>n;
        for(int i=1; i<=n; i++)
        {
            cin>>x;
            rp[x]=i;
        }
        for(int i=1; i<=n; i++)
        {
            cin>>x;
            a[i]=rp[x];
        }
        msort(1,n);
        cout<<ans<<endl;
    }
    return 0;
}

2.树状数组求逆序对:

关于树状数组求逆序对的问题,自己可以说是从零学了一遍树状数组。

参考:算法学习(二)——树状数组求逆序数

树状数组彻底入门,算法小白都看得懂的超详细解析

自己补题的时候还遇到一个坑点就是ans应该是long long,不然就WA,因为ans的最大值应该是\sum _{i=1}^ni (n=1e5)

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n;
int a[maxn]; 
int bit[maxn];
int sum(int i){
	int s=0;
	while(i>0){
		s+=bit[i];
		i-=i&(-i);
	}
	return s;
}
void add(int i,int x){
	while(i<=n){
		bit[i]+=x;
		i+=i&(-i);
	}
}
int main(){
//	freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	int temp;
	long long ans;
	cin>>t;
	while(t--){
		ans=0;
		memset(bit,0,sizeof(bit));
		cin>>n;
		//下标从1开始是因为树状数组的下标从1开始 
		for(int i = 1 ; i <= n ; i ++){
			cin>>temp;
			a[temp]=i;//元素出现在这个位置 
		}
		for(int i = 1 ; i <= n ; i ++){
			cin>>temp;
			//sum求出的是比当前元素小且已经在当前元素前出现过的元素个数 
			//i是指包括当前元素,已经出现的元素个数
			//所以对当前元素来说,逆序数就是已经在它前面出现的比它大的元素个数
			//暨 当前元素个数i-当前元素本身1-比当前元素小的元素个数sum(mp[temp]) 
			ans+=i-1-sum(a[temp]);
			add(a[temp],1);//这个位置已经出现过元素 
			//以上两条语句如果换位置则ans+=i-sum(mp[temp]); 
		}
		cout<<ans<<endl;
	}
	return 0;
}

 

截至目前,尚未有2024年四川省级大学生程序设计竞赛的具体目或解公开发布。然而,可以基于以往的比赛内容推测可能涉及的类型和主。 通常情况下,四川省大学生程序设计竞赛(Sichuan Provincial Collegiate Programming Contest)中的A往往是一个相对基础但具有挑战性的算法问,旨在测试参赛者的逻辑思维能力和编程技巧。例如,在2021年的比赛中,A“Chuanpai”的核心在于通过模拟来处理周期性变化的行为模式[^3]。 对于类似的目,解决的关键通常是识别并利用某种规律或者周期特性来进行高效计算。以下是针对该类问的一个通用解决方案框架: ### 解决方案框架 假设未来某道A涉及到周期行为的变化,则可以通过如下方式实现其基本逻辑: #### 周期检测与状态更新 ```python def simulate_rounds(people, preferences): rounds = 0 while not is_stable_state(people): # 判断当前状态是否稳定 update_people_based_on_preferences(people, preferences) # 更新每个人的状态 rounds += 1 return rounds, people def is_stable_state(people): # 定义稳定性条件 pass def update_people_based_on_preferences(people, preferences): n = len(preferences) new_people = [] for i in range(n): if (preferences[i] % 2 == 0 and i % 2 == 0) or \ (preferences[i] % 2 != 0 and i % 2 != 0): new_people.append(add_dish(people[i])) else: new_people.append(eat_dish(people[i])) global people people = new_people[:] def add_dish(person): person['dishes'] += 1 return person def eat_dish(person): if person['dishes'] > 0: person['dishes'] -= 1 return person ``` 上述代码片段展示了如何根据偏好列表`preferences`动态调整人员数组`people`的状态,并持续迭代直到达到某个稳定的终止条件为止。 尽管目前无法确切得知2024年度的确切考细节,但从过往经验来看,比赛倾向于考察选手们对数据结构、算法优化以及边界情况处理的理解程度。 ### 结论 综上所述,虽然具体到2024年的赛事详情尚不可知,但是通过对历年真的学习研究可以帮助预测可能出现的方向及其应对策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值