[POI2012]LIT-Letters

对于相邻交换变成一个指定的字符串或者数串等,要求最少交换次数,一般来说都可以用逆序对来处理。

逆序对的求法有三种:

  • 暴力 O(n2)
  • 树状数组
  • 归并排序

对于这道题而言,暴力优化能给你 70 分的好成绩,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
char A[N],B[N];int B1[N],cntchar[30],jump[N],Start[30],Last[30];//Start记录每个大写字母第一次出现的位置,jump即下一个相同的大写字母在什么位置,Last只是服务找出jump的一个工具,cntchar是每个大写字符出现的次数。
map<char,int> change;
int main(){
	int n,cnt=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		cin>>A[i];
		if(!change[A[i]]) cnt++,change[A[i]]=cnt; 
	}
	for(int i=1;i<=n;i++){
		cin>>B[i];
		B1[i]=change[B[i]];
		cntchar[B1[i]]++;
		if(!Start[B1[i]]){
			Start[B1[i]]=i,Last[B1[i]]=i;
			continue;
		} 
		jump[Last[B1[i]]]=i;
		Last[B1[i]]=i;
	}
	for(int i=1;i<=26;i++) jump[Last[i]]=n+1;
	long long answer=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=B1[i]-1;j++){
			int ans=0;
			for(int x=Start[j];x<=i;x=jump[x]) ans++;
			answer+=(long long)(cntchar[j]-ans);
		}
	}
	printf("%lld",answer);
	return 0;
} 

举例说明:

A B C B
C B A B

每个 jumpi 如图:

i 1 2 3 4
jumpi 5 4 5 5

再根据逆序对的定义,ijsisj 的性质应用到了代码中的:

answer+=(long long)(cntchar[j]-ans);

可自行体会,时间复杂度还是有点牵强,所以不推荐。

树状数组

因为这个会有重复的大写字母,所以为了答案最优,必须将每个相同字母设为不同的数,而不能是相同的数。

举例说明:

A B B C
1 2 3 4
B A B C
2 1 3 4

对于树状数组求逆序对的方法与原理的详解请移步至https://blog.csdn.net/SSimpLe_Y/article/details/53744096?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164761347516781683950013%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164761347516781683950013&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-6-53744096.142v2pc_search_result_cache,143v4control&utm_term=%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84%E6%B1%82%E9%80%86%E5%BA%8F%E5%AF%B9&spm=1018.2226.3001.4187

参考代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
char A[N],B[N];int s[26][N],B1[N],cnt[26],n,pre[N];
int lowbit(int x){
	return x&(-x);
}
void insert(int x){
	while(x<=n){
		pre[x]++;
		x+=lowbit(x);	
	}
}
long long query(int x){
	long long answer=0;
	while(x){
		answer+=pre[x];
		x-=lowbit(x);	
	}
	return answer;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		cin>>A[i];
		s[A[i]-'A'][++s[A[i]-'A'][0]]=i;
	} 
	for(int i=1;i<=n;i++){
		cin>>B[i];
		B1[i]=s[B[i]-'A'][++cnt[B[i]-'A']];
	}
	long long answer=0;
	for(int i=1;i<=n;i++){
		answer+=query(n)-query(B1[i]-1);
		insert(B1[i]); 
	}
	printf("%lld",answer);
	return 0;
} 

还是原来那句话:

对于相邻交换变成一个指定的字符串或者数串等,要求最少交换次数,一般来说都可以用逆序对来处理。

posted @   SHAConan  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示