题目大意:
给你一个长度为 $n$ 的字符串,每次可以交换相邻的两个字符,求最小的交换次数使得将字符串变成回文串($n\le 10^6$)。
可以考虑终止状态和起始状态的联系,可以构建序列 b 表示依次给每个字符找到原序列中的对应字符的出现位置,答案即为 b 数组的逆序对数。
例如:
构建出来的序列为 1 2 4 3 5
,所以答案为 $1$。
那么我们就将问题转化为求出一个序列使得它对应上去了以后逆序对数最小。
考虑任意两种字符之间的贡献不难发现,我们可以钦定所有字符的左边不动,只改变右边。于是我们可以构造出一个新的序列。然后求一波逆序对即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| #include<bits/stdc++.h> using namespace std; #define il inline #define rg register #define int long long char s[1000005]; il int read() { int re=0,k=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')k=-1;ch=getchar();} while(ch<='9'&&ch>='0'){re=re*10+ch-48;ch=getchar();} return re*k; } il void write(int x) { if(x<0)return putchar('-'),write(-x),void(); if(x<10)return putchar(x+48),void(); return write(x/10),putchar(x%10+48),void(); } int n,t[26],p[1000005],r[26],b[1000005]; int tree[1000005],cnt; bool vis[1000005]; void add(int x) { while(x<=n) { tree[x]++; x+=x&-x; } } int sum(int x) { int re=0; while(x) { re+=tree[x]; x-=x&-x; } return re; } int abs2(int x) { return x>0?x:-x; } signed main() { scanf("%s",s+1); n=strlen(s+1); for(rg int i=1;i<=n;i++) { int now=s[i]-'A'; t[now]++; p[i]=r[now]; r[now]=i; } for(rg int i=0;i<26;i++) { if(t[i]&1) { if(cnt) { puts("-1");return 0; } cnt=i; } } if(cnt) { int now=r[cnt],las=0; for(int i=1;i<=t[cnt]/2;i++) { now=p[now]; } if(las) p[las]=p[now]; b[n/2+1]=now; vis[now]=1; } int ans=0,ss=0; for(rg int i=1;i<=n/2+ss;i++) { if(vis[i]){ss++;continue;} int now=s[i]-'A'; vis[i]=vis[r[now]]=1; b[i-ss]=i;b[n-i+ss+1]=r[now]; r[now]=p[r[now]]; } for(int i=n;i>=1;i--) { ans+=sum(b[i]); add(b[i]); } write(ans); }
|