CF1268C K Integers(杂题选做)

· · 题解

前言

挺好的一道 DS 题。

摘自 杂题选做。

思路分析

首先因为要输出 n 个数,所以枚举这 n 的情况是肯定少不了了。

接着来考虑问题,其实就是: 这样一个情况,其中黑框表示 1\sim n 所有数,蓝框表示包住 1\sim k 所有数的最小框,而红框是我们最后把数移进去的样子。

那么这个题就被切割为了两个部分:

  1. 把这些数移动进去。
  2. 进行排序。

套路的考虑把这两部分分开考虑。

首先我们知道的是,在 1 中如果多用了步数去交换两个需要的数而不是单纯的移动到一起,这一步交换就可以放进 2 中。

也就是 1\sim k 这些数的偏序关系是不变的。

那么根据冒泡排序的结论,交换次数就是逆序对个数。

数逆序对当然很擅长,直接考虑用 BIT 维护即可。

因为动态插入数,就相当于是一个区间加,数一下即可。

接着来考虑处理比较不熟悉的部分 1

其实也还算比较简单,考虑数学中应该都曾做过一类题:

|x-a_1|+|x-a_2|+|x-a_3|(a_1\le a_2\le a_3) 最小值。

这种题的结论也是显而易见的:

  1. 如果奇数个,x 就取最中间的那个 a 的值。
  2. 如果偶数个,x 就取中间相邻两个数的任意一个值。

这两个问题相似却又有不同,在这个问题中,我们的 x 只能取到一个存在点的值。

类似的思考,不难发现最优决策即是固定最中间一个数不动,让左右两边的数朝中间靠拢。

那怎么找出这个最中间的数呢?

因为有单调性,所以直接二分即可。

check 也用 BIT 就可以轻松实现了。

因为左右相同,所以这边只讲左边。

考虑固定的点是 a_x,那么左边的数就有 a_1,\dots,a_{x-1}

而他们应该移到的位置是 a_x-(x-1),\dots,a_x-1

移动的代价就是两两作差,交换律一下就是下面之和减上面之和。

上面的那个和可以直接用 BIT 维护出来,只需要考虑下面的和。

先给 a_x 有关部分都提出来,剩下的就是个等差数列,然后就做完了。

代码

#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;
}