1. 程式人生 > >JZOJ5958. 【NOIP2018模擬11.8A組】祕密郵件

JZOJ5958. 【NOIP2018模擬11.8A組】祕密郵件

Description

小 A 收到了一封來自外星球的祕密郵件。郵件由 n(n≤1000000) 個大寫英文字母組成,不巧的是小A 收到郵件以後一不小心打亂了原來的字母順序。但是聰明的小 A 記住了原郵件的完整內容,現在她每次可以選擇打亂後的郵件中相鄰的兩個字母進行交換,問最少交換多少次能夠將打亂的郵件恢復成原郵件。

題解

考慮將問題轉化為序列上面的問題, 我們將目標串視作是一個有序的序列,而且就認為是1到n。 考慮如何將原串對應, 顯然是相同字母的位置相對於, 如果相同字母很多的話,顯然是第一個對應第一個,第二個對於第二個,如此類推。 考慮如果第一個對應第二個,第二個對於第一個,這樣他們在交換的時候就會有重複的,顯然是不優的。 現在考慮如何求最小交換次數, 可以知道,每次交換可以減少一個逆序對的個數, 於是答案就是逆序對個數。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
char ch;
void read(int&n)
{
	for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
	for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
const int N=1000003;
int n,t[26],w[26],a[N],v[26][N],s[N];
long long ans;
int x_(int x){return x&(-x);}
void ins(int x){for(int i=x;i<=n;i=i+x_(i))s[i]++;}
int find(int x){int S=0;for(int i=x;i;i=i-x_(i))S=S+s[i];return S;}
int main()
{
	freopen("letter.in","r",stdin);
	freopen("letter.out","w",stdout);
	read(n);
	for(ch=getchar();ch<'A' || ch>'Z';ch=getchar());
	for(int i=1;i<=n;i++)v[ch-'A'][++t[ch-'A']]=i,ch=getchar();
	for(;ch<'A' || ch>'Z';ch=getchar());
	for(int i=1;i<=n;i++)
	{
		a[i]=v[ch-'A'][++w[ch-'A']],ch=getchar();
		ans=ans+i-find(a[i])-1;ins(a[i]);
	}
	printf("%lld\n",ans);
}