给出如下排序长度为 l e n len len的数组代码:
for(int i=1;i<=len;i++)
for(int j=1;j<=len;j++)
if(a[i]<a[j]) swap(a[i],a[j]);
给出一个长 n n n的数组 a a a,求排序 [ 1 , k ] [1,k] [1,k]区间的交换次数, k = 1 , 2 , . . . . , i k=1,2,....,i k=1,2,....,i
显然我们需要模拟一便这个排序,不妨直接写一个这个程序,输出。
发现排序长度为 l e n len len时,有如下性质,第 i i i次会把最大值交换到 i i i处,且在第一次之后,由于从从前往后交换,最大值目前位于 i − 1 i-1 i−1处,于是这一次发生的交换次数为 [ 1 , i − 1 ] [1,i-1] [1,i−1]大于 a [ i ] a[i] a[i]的数的种数,因为相等的元素不交换。这个是可以用树状数组维护的,但这样的时间复杂度是 O ( n 2 ) O(n^2) O(n2)的,不妨考虑一下相邻两个前缀有什么关系,下面考虑两个前缀 [ 1 , i − 1 ] [1,i-1] [1,i−1]和 [ 1 , i ] [1,i] [1,i]的关系,并且叫第一次暴力处理时发生交换的位置为 [ p 1 , p 2 , . . . , p m ] [p_{1},p_{2},...,p_{m}] [p1,p2,...,pm],最后的结果的 p i p_{i} pi的会到 p i + 1 p_{i+1} pi+1去, p m p_{m} pm会到 p 1 p_{1} p1去,暴力处理之后的每一次都是把最大值交换到 i i i就不发生交换了,不难发现 [ p 1 , p 2 , . . . , p m ] [p_{1},p_{2},...,p_{m}] [p1,p2,...,pm]这些位置就是前缀最大值的变化点,于是发生变化一定在此处,即 a [ i ] > m a x ( a [ 1 ] , a [ 2 ] , . . . , a [ i − 1 ] ) a[i]>max(a[1],a[2],...,a[i-1]) a[i]>max(a[1],a[2],...,a[i−1])时,这样比较复杂,不妨按照这样的思路,先考虑:
此时最大值已经在 [ 1 , i − 1 ] [1,i-1] [1,i−1]被找出来了,所以我们只需要在最大值在 i − 1 i-1 i−1时把最大值移到 i i i即可,即
a n s [ i ] = a n s [ i − 1 ] + c o u n t ( a [ i ] + 1 , n ) ans[i]=ans[i-1]+count(a[i]+1,n) ans[i]=ans[i−1]+count(a[i]+1,n)
其中 c o u n t ( x , y ) count(x,y) count(x,y)是 [ 1 , i − 1 ] [1,i-1] [1,i−1]中有多少数是 ∈ [ x , y ] \in[x,y] ∈[x,y]的,这个可以用树状数组同时维护
a n s [ i ] = a n s [ i − 1 ] ans[i]=ans[i-1] ans[i]=ans[i−1]
由于 [ 1 , i − 1 ] [1,i-1] [1,i−1]的最大值不为 a [ i ] a[i] a[i],但是还是存在一个最大值,替 a [ i ] a[i] a[i]完成了交换工作,只需要在第一次暴力处理的时候把 a [ i ] a[i] a[i]交换到1,就完成了把最大值更新为 a [ i ] a[i] a[i]的工作,又最大值此时要从 i − 1 i-1 i−1被交换到 i i i处,这两个工作需要2的交换次数。并且由于修改最大值,之前的最大值有多个的情况下互相不会计数,而现在他们都需要和现在的最大值计数了,并且最大值第二次出现的位置pos之后的区间 [ p o s , i − 1 ] [pos,i-1] [pos,i−1]每个数都要与新最大值交换,于是此时有
$$
ans[i]=ans[i-1]+f[max(1,i-1)]*(i-1-second[max(1,i-1)]+1)+2
$$
这个思维难度有点大。。
// #include
#include
#include
#include
#include
#include
#include
#include
#include
#define endl '\n'
using namespace std;
using ll=long long;
const int N=100005,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353;
int a[N],tot[N];
ll n,ans[N],tree[N];
bool vis[N];
inline int lowbit(int x){return -x&x;}
void add(int x,int val)
{
if(vis[x]) return;
vis[x]=true;
while(x<=n)
{
tree[x]+=val;
x+=lowbit(x);
}
}
ll getsum(int x)
{
ll ret=0;
while(x)
{
ret+=tree[x];
x-=lowbit(x);
}
return ret;
}
int first[N],second[N];
ll query(int l,int r){return getsum(r)-getsum(l-1);}
void work()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
tree[i]=vis[i]=tot[i]=first[i]=second[i]=0;
}
int max1=a[1];
add(a[1],1); first[a[1]]=1;
for(int i=2;i<=n;i++)
{
if(a[i]==max1) ans[i]=ans[i-1];
else if(a[i]<max1) ans[i]=ans[i-1]+query(a[i]+1,n);
else ans[i]=ans[i-1]+(second[max1]!=0)*(i-second[max1])+2;
add(a[i],1); max1=max(max1,a[i]);
if(!first[a[i]]) first[a[i]]=i;
else if(!second[a[i]]) second[a[i]]=i;
}
for(int i=1;i<=n;i++)
{
cout<<ans[i];
if(i<n) cout<<" ";
add(a[i],-1);
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(false);
int t; cin>>t;
while(t--) work();
return 0;
}