Codeforces Round #609 (Div. 1)C. K Integers(逆序对二分向中聚集)

Posted Lnn.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #609 (Div. 1)C. K Integers(逆序对二分向中聚集)相关的知识,希望对你有一定的参考价值。

前言:还行,推出了个大概,就差临门一脚。


题目传送门


  题目类型:逆序对、二分、向中聚集。
  解析: 很明显,求f(n)的时候就是求整个数组的逆序对。顺着这样的思路,f(k)就是把1到k聚集到一起,然后求1~k的逆序对。然后我是从fn求到f1的。
  那么题目分成两步了,第一步把每个数字对其后面数生成的逆序对数量找出来,记在suf[a[i]]里。
  第二步就是求聚集k个数的花费。最优解是最中间的数不动,其他数向中间靠拢。(偶数时中间两个都要试一下)
  如何找到最中间的呢?假设求f(k),现在数字分为两种,1到k是我们要聚集的,其他数是无用的。设前缀和pre[i]为1~i中有多少个需要聚集的数,若pre[i] = k/2+k%2 && 位置i的数是需要聚集的,则i位置就是最中间的数。可以二分判断前缀和大小来找i的位置,由于在不断求f(1 ~ n)的过程中,前缀和是变化的,所以用树状数组代替前缀和。复杂度n * log * log。
  最后的一步,求聚集的花费,假设左边有x个数,左边第i个数位置在pos[i],左边的第一个数聚集到中间:pos[mid] - pos[1] - (x-1) ,第二个:pos[mid] - pos[2] - (x-2)…所以总和起来就是 【x*pos[mid]】 - 【pos[1] + pos[2]…+ pos[x]】 - 【0~x-1】。第一个直接算,第二个树状数组维护位置和,第三个等差数列求和。

  code:

#include <bits\\stdc++.h>
#define Lnnnb return 0;
#define ll long long
#define endl '\\n'
#define mem(a) memset(a,0,sizeof(a))
#define maxn 404040

using namespace std;
void eninit();
ll n,a[maxn],id[maxn],suf[maxn];
vector<ll>ans;

struct Tree_array
    ll cmax = 400001 , c[maxn];
    void add(ll x,ll val)
        for(ll i = x ; i <= cmax ; i += (i&-i))
            c[i] += val;
    
    ll query(ll x)
        ll res = 0;
        for(ll i = x ; i >= 1 ; i -= (i&-i))
            res += c[i];
        return res;
    
c,num,val;

void scan()
    cin >> n ;
    for(ll i = 1 ; i <= n ; ++i)cin >> a[i] ;

ll binary(ll aim)
    ll l = 1 , r = n;
    while(l <= r)
        ll mid = (l+r)/2;
        if(num.query(mid) >= aim)r = mid-1;             ///空的位置要左移
        else l = mid+1;
    
    return r+1;

ll coun(ll now , ll lmid , ll flagl , ll flagr)
    ll res = 0 , l = now/2-flagl , r = now/2-flagr;
    ///左边点的数量*中点位置 - 左边位置和 - (0~lmid-1)的和
    res += l * lmid;
    res -= val.query(lmid-1);
    res -= (1 + l)*(l)/2;
    ///右边
    res += val.query(n) - val.query(lmid);
    res -= r * lmid;
    res -= (1 + r)*(r)/2;
    return res;


void solve()
    ll cos1 = 0 , cos2 = 0;
    for(ll i = n ; i >= 1 ; --i)
        id[ a[i] ] = i;
        suf[ a[i] ] = c.query(a[i]);
        cos1 += suf[a[i]];
        c.add(a[i] , 1);
        num.add(i , 1);                         ///num记录的是剩下的数
        val.add(i , i);                         ///val记录位置总和
    
    for(ll i = n ; i >= 1 ; --i)
        ans.push_back(cos1 + cos2);

        cos2 = 0;
        num.add(id[i] , -1);                    ///去掉i
        val.add(id[i] , -id[i]);

        ll now = i-1;
        ll lmid = binary( (now)/2+now%2 );            ///找到剩下的数的左中点

        cos2 = coun(now,lmid,now%2==0,0);
        if(now%2 == 0)cos2 = min(cos2 , coun(now , binary(now/2+1) , 0, 1));

        cos1 -= suf[i];
    
    reverse(ans.begin() , ans.end());
    for(ll i = 0 ; i < ans.size() ; ++i)cout << ans[i] << " " ;


int main()
    ios::sync_with_stdio(false);   cin.tie(0);cout.tie(0);
    ll t = 1;
    ///cin >> t ;
    while(t--)
        scan();
        solve();
        eninit();
    
    Lnnnb


void eninit()



以上是关于Codeforces Round #609 (Div. 1)C. K Integers(逆序对二分向中聚集)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #609 题解

Codeforces Round #609 (Div. 2)

Codeforces Round 609 Div2

Codeforces Round #609 (Div. 2)--B.Modulo Equality

Codeforces Round #609 (Div. 2) A-E简要题解

Codeforces Round #609 (Div. 2)