2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对)

2023-05-13,,

2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对)

https://www.luogu.com.cn/problem/P5041

题意:

给一个字符串 \(S\) ,每次交换相邻两个位置的字符,使得 \(S\) 变成回文串,求最小交换次数。

分析:

首先,对于一个回文串,最多有一个字符出现次数为奇数,这个字符必须放在回文串的最中间,剩下的字符两边放就行。

对于一个字符 \(x\) ,一共在回文串中出现了 \(sizei_x\) 次,每次出现的位置为 \(pos_{x_i}\) 。这 \(sizei_x\) 个字符分布在中间字符(也可能没有中间字符,如果这个字符串长度为偶数)两侧,可想而知,每次移动最左边和最右边的两个字符把它们固定在对称的位置上是最优的,不然就像蓝书第一题,两只蚂蚁来回掉头就是两只蚂蚁一直前进一样。

好吧,假设一下,设字符 \(x\) 一共有4个,分别在 \(pos_{x_1}\) 、 \(pos_{x_2}\) 、 \(pos_{x_3}\) 、 \(pos_{x_4}\) ,且 \(pos_{x_1}<pos_{x_2}<pos_{x_3}<pos_{x_4}\) ,它们可以被固定在两个轴对称的位置 \(a\) 与 \(\alpha\) 、 \(b\) 与 \(\beta\) ,且 \(a<b<\beta<\alpha\) 。如果把 \(pos_{x_2}\) 与 \(pos_{x_3}\) 这两个位置上的 \(x\) 固定到 \(a\) 与 \(\alpha\) ,那么 \(pos_{x_2}\) 上的 \(x\) 一定会和 \(pos_{x_1}\) 上的交换,不如直接从 \(pos_{x_1}\) 开始交换,对于 \(pos_{x_3}\) 上的 \(x\) 同理。

对于两个相邻位置上的字符 \(x\) 与 \(y\) , \(x\) 在 \(y\) 的左边。如果离 \(x\) 最远的另一个相同的字符包含了离 \(y\) 最远的另一个相同字符,即 xyyx ,那么先选 \(x\) 再选 \(y\) 是最优的。如果两个字符交叉出现即 xyxy ,对结果并没有影响。如果两个字符分别重复出现即 xxyy ,对结果依旧没影响。所以按照顺序对字符进行排序最优。

至于一个字符究竟交换了几次?咱把原位置在排完序后的出现次序记录下来,求个逆序对~

树状数组,你值得拥有~

(咱是不是做过同类型的题?/疑惑)

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std; #define int long long
const int N=1e6+10;
int n,a[N],fin[N],t[N],vis[N],top[N],len;
vector<int>pos[30]; inline int lowbit(int x){
return x&-x;
}
inline void add(int x,int k){
for(int i=x;i<=n+5;i+=lowbit(i))t[i]+=k;
}
inline int query(int x){
int ans=0;
for(int i=x;i>0;i-=lowbit(i))ans+=t[i];
return ans;
} signed main(){
IOS;
string s;cin>>s;
n=s.length();
int cnt=0;
for(int i=1;i<=n;i++){
a[i]=s[i-1]-'A'+1;
pos[a[i]].push_back(i);
if(pos[a[i]].size()&1)++cnt;
else --cnt;
}
//if(n%2==0&&cnt)return puts("-1"),0;
//if(n%2==1&&cnt>1)return puts("-1"),0;
if(cnt){
int now=0;
for(int i=1;i<=26;i++)if(pos[i].size()&1){
now=pos[i][pos[i].size()/2];
vis[now]=1;
fin[n/2+1]=now;
//cout<<3/2<<endl;
//cout<<now<<" "<<pos[i][0]<<" "<<pos[i][1]<<" "<<pos[i][2]<<" "<<pos[i][3]<<" "<<pos[i].size()<<endl;
break;
}
}
for(int i=1;i<=n/2+len;i++){
if(vis[i]){
++len;continue;
}
int x=a[i];
vis[i]=vis[pos[x].back()]=1;
fin[i-len]=i;fin[n-(i-len)+1]=pos[x].back();
pos[x].pop_back();
}
//for(int i=1;i<=n;i++)cout<<fin[i]<<" ";cout<<endl;
int tot=0;
for(int i=n;i>=1;i--){
tot+=query(fin[i]);
add(fin[i],1);
}
cout<<abs(tot);
return 0;
}

2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对)的相关教程结束。

《2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对).doc》

下载本文的Word格式文档,以方便收藏与打印。