2021icpc南京 D

题意:

给出如下排序长度为lenlenlen的数组代码:

for(int i=1;i<=len;i++)
    for(int j=1;j<=len;j++)
        if(a[i]<a[j]) swap(a[i],a[j]);

给出一个长nnn的数组aaa,求排序[1,k][1,k][1,k]区间的交换次数,k=1,2,....,ik=1,2,....,ik=1,2,....,i

Solution:

显然我们需要模拟一便这个排序,不妨直接写一个这个程序,输出。

发现排序长度为lenlenlen时,有如下性质,第iii次会把最大值交换到iii处,且在第一次之后,由于从从前往后交换,最大值目前位于i−1i-1i1处,于是这一次发生的交换次数为[1,i−1][1,i-1][1,i1]大于a[i]a[i]a[i]的数的种数,因为相等的元素不交换。这个是可以用树状数组维护的,但这样的时间复杂度是O(n2)O(n^2)O(n2)的,不妨考虑一下相邻两个前缀有什么关系,下面考虑两个前缀[1,i−1][1,i-1][1,i1][1,i][1,i][1,i]的关系,并且叫第一次暴力处理时发生交换的位置为[p1,p2,...,pm][p_{1},p_{2},...,p_{m}][p1,p2,...,pm],最后的结果的pip_{i}pi的会到pi+1p_{i+1}pi+1去,pmp_{m}pm会到p1p_{1}p1去,暴力处理之后的每一次都是把最大值交换到iii就不发生交换了,不难发现[p1,p2,...,pm][p_{1},p_{2},...,p_{m}][p1,p2,...,pm]这些位置就是前缀最大值的变化点,于是发生变化一定在此处,即a[i]>max(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[i1])时,这样比较复杂,不妨按照这样的思路,先考虑:

a[i]<max(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[i1])时:

此时最大值已经在[1,i−1][1,i-1][1,i1]被找出来了,所以我们只需要在最大值在i−1i-1i1时把最大值移到iii即可,即

ans[i]=ans[i−1]+count(a[i]+1,n) ans[i]=ans[i-1]+count(a[i]+1,n) ans[i]=ans[i1]+count(a[i]+1,n)

其中count(x,y)count(x,y)count(x,y)[1,i−1][1,i-1][1,i1]中有多少数是∈[x,y]\in[x,y][x,y]的,这个可以用树状数组同时维护

a[i]=max(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[i1])时,最大值都不需要移到iii,于是:

ans[i]=ans[i−1] ans[i]=ans[i-1] ans[i]=ans[i1]

最后再来考虑a[i]>max(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[i1])

由于[1,i−1][1,i-1][1,i1]的最大值不为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−1i-1i1被交换到iii处,这两个工作需要2的交换次数。并且由于修改最大值,之前的最大值有多个的情况下互相不会计数,而现在他们都需要和现在的最大值计数了,并且最大值第二次出现的位置pos之后的区间[pos,i−1][pos,i-1][pos,i1]每个数都要与新最大值交换,于是此时有

$$
ans[i]=ans[i-1]+f[max(1,i-1)]*(i-1-second[max(1,i-1)]+1)+2

$$

这个思维难度有点大。。

// #include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<set>
#include<queue>
#include<cmath>
#include<cstring>
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值