M. Youth Finale
题目大意就是首先求出给出序列的逆序对,然后根据两个不同的操作,对序列进行改变后,再求它的逆序列。
第一个操作思维
首先可以肯定的是这序列是由1~n组成的,由此,当我们执行左移循环操作时,假设当前序列头是a0该序列会减少(a0-1)个,(因为能构成逆序对的必然是在它之后且比他小的数),会增加(n-a0)个,当它到尾巴后,显然能构成逆序列的就只有比它大的数了。
s
u
m
−
=
a
0
−
1
;
s
u
m
+
=
n
−
a
0
sum-=a_0 -1;sum+=n-a0
sum−=a0−1;sum+=n−a0
第二个操作思维
由于倒过来了,那么逆序对就会变成正序对,而正序对就会变成逆序对,我们只需要求出正序对和逆序对之和即可。
s
u
m
=
n
∗
(
n
−
1
)
2
sum = \frac{n*(n-1)}{2}
sum=2n∗(n−1)
再用sum - 当前的逆序对之和。即可得到翻转后的逆序对数目。
整体设计思维
首先要求出初始序列的逆序对数目,我们可以通过树状数组求逆序对。不了解的可以去学习一下树状数组是什么。
当求出初始逆序对数目之后,只需要考虑前两个思维,加上当前序列的头是谁(也就是维护头),即可求解。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int lim = 6e5+10;
int count_[lim];
int a[lim];
int getLowOneBit(int t)
{
return t&-t;
}
void add_(int t)
{
while(t<=n)
{
count_[t]++;
t+=getLowOneBit(t);
}
}
int get_(int t)
{
int ans=0;
while(t>=1)
{
ans+=count_[t] ;
t-=getLowOneBit(t);
}
return ans;
}
signed main(){
cin>>n>>m;
//使用树状数组求逆序对
int sum=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
add_(a[i]);
//求出在它之前比他小的树的数量a,就可以用当前所有元素的数量- a即可得到在它之前,比他大的数量
sum+=i -get_(a[i]);
}
cout<<sum<<"\n";
string s;
cin>>s;
int sum2 = n*(n-1)/2;//逆序对和正序对的总和
int head = 1;
bool dir = 1;
for(int i=0;i<s.length();i++)
{
if(s[i]=='S')
{
sum-=a[head]-1;
sum+=n - a[head];
if(dir) head++;
else head--;
}
else
{
//逆序对数量和正序对数量,两者交换
sum = sum2 - sum;
if(dir)head--;
else head++;
dir = !dir;
}
if(head==0) head=n;
if(head==n+1) head=1;
cout<<sum%10;
}
}