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 (Div. 2)
Codeforces Round #609 (Div. 2)--B.Modulo Equality