CF1268C K Integers(杂题选做)
前言
挺好的一道 DS 题。
摘自 杂题选做。
思路分析
首先因为要输出
接着来考虑问题,其实就是:
这样一个情况,其中黑框表示
那么这个题就被切割为了两个部分:
- 把这些数移动进去。
- 进行排序。
套路的考虑把这两部分分开考虑。
首先我们知道的是,在
也就是
那么根据冒泡排序的结论,交换次数就是逆序对个数。
数逆序对当然很擅长,直接考虑用 BIT 维护即可。
因为动态插入数,就相当于是一个区间加,数一下即可。
接着来考虑处理比较不熟悉的部分
其实也还算比较简单,考虑数学中应该都曾做过一类题:
求
|x-a_1|+|x-a_2|+|x-a_3|(a_1\le a_2\le a_3) 最小值。
这种题的结论也是显而易见的:
- 如果奇数个,
x 就取最中间的那个a 的值。 - 如果偶数个,
x 就取中间相邻两个数的任意一个值。
这两个问题相似却又有不同,在这个问题中,我们的
类似的思考,不难发现最优决策即是固定最中间一个数不动,让左右两边的数朝中间靠拢。
那怎么找出这个最中间的数呢?
因为有单调性,所以直接二分即可。
check 也用 BIT 就可以轻松实现了。
因为左右相同,所以这边只讲左边。
考虑固定的点是
而他们应该移到的位置是
移动的代价就是两两作差,交换律一下就是下面之和减上面之和。
上面的那个和可以直接用 BIT 维护出来,只需要考虑下面的和。
先给
代码
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define int long long
using namespace std;
const int N=2e5+10,INF=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
struct BIT//binary_indexed_tree
{
int c[N],n;BIT(){memset(c,0,sizeof c);}
void modify(int x,int v){for(;x<=n;x+=x&-x) c[x]+=v;}
int query(int x){int res=0;for(;x;x-=x&-x) res+=c[x];return res;}
}tt[2];
int n,a[N],pos[N];
namespace Fast_IO
{
static char buf[1000000],*paa=buf,*pd=buf,out[10000000];int length=0;
#define getchar() paa==pd&&(pd=(paa=buf)+fread(buf,1,1000000,stdin),paa==pd)?EOF:*paa++
inline int read()
{
int x(0),t(1);char fc(getchar());
while(!isdigit(fc)){if(fc=='-') t=-1;fc=getchar();}
while(isdigit(fc)) x=(x<<1)+(x<<3)+(fc^48),fc=getchar();
return x*t;
}
inline void flush(){fwrite(out,1,length,stdout);length=0;}
inline void put(char c){if(length==9999999) flush();out[length++]=c;}
inline void put(string s){for(char c:s) put(c);}
inline void print(int x)
{
if(x<0) put('-'),x=-x;
if(x>9) print(x/10);
put(x%10+'0');
}
inline bool chk(char c) { return !(c=='('||c==')'); }
inline bool ck(char c) { return c!='\n'&&c!='\r'&&c!=-1&&c!=' '; }
inline void rd(char s[],int&n)
{
s[++n]=getchar();
while(chk(s[n])) s[n]=getchar();
while(ck(s[n])) s[++n]=getchar();
n--;
}
}
using namespace Fast_IO;
signed main()
{
tt[0].n=tt[1].n=n=read();for(int i=1;i<=n;i++) pos[a[i]=read()]=i;
for(int k=1,s=0;k<=n;k++)
{
s+=k-tt[0].query(pos[k])-1;int l=1,r=n,ans=0;
tt[0].modify(pos[k],1),tt[1].modify(pos[k],pos[k]);
while(l<=r) if(tt[0].query(mid-1)<=k/2) ans=mid,l=mid+1;else r=mid-1;
int l1=tt[0].query(ans),l2=tt[1].query(ans),r1=k-l1,r2=tt[1].query(n)-l2;
print(s+ans*l1-l2-l1*(l1-1)/2+r2-ans*r1-r1*(r1+1)/2);put(' ');
}
genshin:;flush();return 0;
}