Codeforces Round #609 Div2

1269E K Integers(数据结构)

题意:

​ 给一个1-n的排列,每进行一次操作可交换序列中相邻的两数,设 f ( k ) f(k) f(k)为使序列出现连续的1,2,…,k所需要的最小操作数,求所有的 f ( k ) , k = 1 , 2 , . . . , n   ( n ≤ 2 ∗ 1 0 5 ) f(k),k=1,2,...,n\ (n\le 2*10^5) f(k),k=1,2,...,n (n2105)

解法:

​ 当一段长度为m的子序列为一个1-m的排列时,显然 f ( m ) f(m) f(m)为子序列中逆序对总个数。于是进一步考虑求 f ( k ) f(k) f(k)时,将1-k这k个数字”聚拢“成连续的一段所需要的操作数。可以证明,以位于最中间的数字为“中心”聚拢时交换次数最小。设1-k在原序列中的位置为 p 1 , p 2 , . . . , p k p_1,p_2,...,p_k p1,p2,...,pk,中心位置为 l l l r = l + 1 r=l+1 r=l+1,整个序列被分为两部分: [ 1 , l ] 、 [ r , n ] [1,l]、[r,n] [1,l][r,n]。设位于 [ 1 , l ] [1,l] [1,l] p i p_i pi l c n t lcnt lcnt个,位于 [ r , n ] [r,n] [r,n] p i p_i pi r c n t rcnt rcnt个,则将位于 [ 1 , l ] [1,l] [1,l] p i p_i pi交换至 [ l − l c n t + 1 , l ] [l-lcnt+1,l] [llcnt+1,l],交换次数为 l ∗ l c n t − ∑ p i ≤ l p i − ( 1 + 2 + . . . + l c n t − 1 ) l*lcnt-\sum\limits_{p_i\le l} p_i-(1+2+...+lcnt-1) llcntpilpi(1+2+...+lcnt1),同理对 [ r , n ] [r,n] [r,n]中的 p i p_i pi交换次数为 ∑ p i ≥ r p i − r ∗ r c n t − ( 1 + 2 + . . . + r c n t − 1 ) \sum\limits_{p_i\ge r} p_i-r*rcnt-(1+2+...+rcnt-1) pirpirrcnt(1+2+...+rcnt1)

​ 综上,我们只需在k从1到n递增的过程中动态维护 l 、 l c n t 、 ∑ p i ≤ l p i l、lcnt、\sum\limits_{p_i\le l} p_i llcntpilpi。利用一个大根堆和一个小根堆可实现动态维护序列 p i p_i pi中位数 l l l。再分别用两个树状数组 t r e e 1 、 t r e e 2 tree1、tree2 tree1tree2维护 l c n t lcnt lcnt ∑ p i ≤ l p i \sum\limits_{p_i\le l} p_i pilpi,修改时在位置 p i p_i pi上, t r e e 1 tree1 tree1每次增加1, t r e e 2 tree2 tree2每次增加 p i p_i pi。求逆序对数利用 t r e e 1 tree1 tree1即可。

​ 复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=2e5+5;
int n,p[maxn],idx[maxn];
ll tree1[maxn],tree2[maxn];
priority_queue<int>lhp;
priority_queue<int,vector<int>,greater<int> >rhp;
void update(int x,int val)
{	while(x<=n) tree1[x]++,tree2[x]+=val,x+=lowbit(x); }
ll query(int x,ll* tree)
{
	ll ans=0;
	while(x) ans+=tree[x],x-=lowbit(x);
	return ans;
}
void insert(int x)
{
	if(!rhp.empty()&&x>=rhp.top())
	{
		lhp.push(rhp.top());
		rhp.pop(),rhp.push(x);
	}
	else lhp.push(x);
	if(rhp.size()+1<lhp.size())
		rhp.push(lhp.top()),lhp.pop();
}
ll sum(ll x)
{	return x*(x+1)/2; }
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>p[i],idx[p[i]]=i;
	ll inv=0;
	for(int i=1;i<=n;i++)
	{
		update(idx[i],idx[i]),insert(idx[i]);
		int l=lhp.top(),r=l+1;
		int lcnt=query(l,tree1),rcnt=i-lcnt;
		inv+=i-query(idx[i],tree1);
		ll ans=inv+query(n,tree2)-2*query(l,tree2);
		ans+=l*lcnt-r*rcnt-sum(lcnt-1)-sum(rcnt-1);
		cout<<ans<<" ";
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值