1 条题解
-
0
对于一种字母,记其出现次数为 。 显然前面出现的 的字母都给它移到前 个字符中,后面出现的 扔到后面 个字符中更优。记录 表示前面已经填了多少个字符,如果当前位置要移到前 个位置中,其耗费的代价为 ,扔到后面 个位置的话不用考虑代价,因为将前 个填满后剩下的自然就到了后面去。
接下来考虑怎么计算使前后两部分相等的代价,记 为扔到前面的 个字符的顺序, 为后面 个的顺序,那么这道题就转化为每次可以交换相邻的两个元素,问最少多少次可以使 序列变成 序列,也就是 P1966,但是需要注意的时相同的字符显然要前面指向前面的,后面指向后面的更优,因为可以减少相对顺序的逆序对数量。 细节不多,主要是后面处理相同字符比较麻烦。//write by szh #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; ll read() { ll x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9') { x=x*10+ch-48; ch=getchar(); } return x*f; } const ll N=2e5+10; ll n; char a[2*N]; ll q1[N],q2[N]; ll head1,head2,ans; ll num[27],ud[27]; ll t[N],A[N],B[N],l[N]; vector<ll> w[27]; ll hw[27]; bool cmp1(ll x,ll y){ return q1[x]<q1[y]; } bool cmp2(ll x,ll y){ return q2[x]<q2[y]; } ll lowbit(ll x){ return x&-x; } void add(ll x){ while(x<=n){ t[x]+=1; x+=lowbit(x); } } ll query(ll x){ ll sum=0; while(x){ sum+=t[x]; x-=lowbit(x); } return sum; } signed main(){ n=read(); scanf("%s",a+1); for (int i=1;i<=2*n;i++) num[a[i]-'a'+1]++; for (int i=1;i<=2*n;i++){ ud[a[i]-'a'+1]++; if(ud[a[i]-'a'+1]<=num[a[i]-'a'+1]/2){ q1[++head1]=a[i]-'a'+1; ans+=i-head1; } else q2[++head2]=a[i]-'a'+1; } for (int i=1;i<=n;i++) A[i]=i,B[i]=i; sort(A+1,A+1+n,cmp1); sort(B+1,B+1+n,cmp2); for (int i=1;i<=n;i++) w[q1[A[i]]].push_back(A[i]); for (int i=1;i<=26;i++) sort(w[i].begin(),w[i].end()); for (int i=1;i<=n;i++) A[i]=w[q1[A[i]]][hw[q1[A[i]]]++]; for (int i=1;i<=26;i++) w[i].clear(),hw[i]=0; for (int i=1;i<=n;i++) w[q2[i]].push_back(i); for (int i=1;i<=26;i++) sort(w[i].begin(),w[i].end()); for (int i=1;i<=n;i++) l[A[i]]=w[q2[B[i]]][hw[q2[B[i]]]++]; for (int i=1;i<=n;i++){ ans+=(i-1-query(l[i])); add(l[i]); } cout<<ans<<"\n"; }
- 1
信息
- ID
- 10860
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者