1515: PIPI的开关Ⅱ 题解(优先队列维护中位数)

题目链接

题目大意

题目描述

PIPI得到了n个整数,每个整数下面有两个开关,其中一个开关能使该数+1,而另一个开关能使该数-1。
现在PIPI想知道,对于每个位置i,使得[1,i]区间的整数变成一段连续的数字,最少需要按多少次开关?
对于在[1,i)区间的每个j,都满足a[j]+1=a[j+1]的话,那么我们认为[1,i]区间的整数是一段连续的数字。特别得,我们规定当i=1,[1,1]区间的整数也是一段连续的数字。

输入

第一行输入一个正整数n,n<=10^5。
第二行n个整数ai,-10^9 <=ai<=10^9。

输出

输出n行,第i行表示将[1,i]区间的整数变成一段连续的数字所要按的最少开关次数。

题目思路

思路还是挺简单的令\(a[i]=a[i]-(i-1)\)

那么就是把\([1,i]\)的所有元素移动到中位数去

我最开始用优先队列找中位数,然后使用线段树动态开点来维护信息进行计算,可以过但是过于麻烦

其实可以直接用优先队列顺便维护下两个队列的信息即可

代码1

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define fi first
#define se second
using namespace std;
const int maxn=2e6+5;
priority_queue<int,vector<int>,less<int> > p1;
priority_queue<int,vector<int>,greater<int> > p2;
int n,k,a[100000+5];
int mid=-1e9-1e6,cnt=1;
pair<ll,ll> tree[maxn];
int lson[maxn],rson[maxn];
pair<ll,ll> query(int node,ll l,ll r,ll ql,ll qr){
    if(ql>qr) return {0,0};
    if(node==0) return {0,0};
    //这里没有被建树,显然没有值
    if(ql<=l&&r<=qr){
        return tree[node];
    }
    ll mid=(l+r)/2;
    pair<ll,ll> sum={0,0};
    if(mid>=ql) {
        pair<ll,ll> tmp=query(lson[node],l,mid,ql,qr);
        sum.fi+=tmp.fi,sum.se+=tmp.se;
    }
    if(mid<qr){
        pair<ll,ll> tmp =query(rson[node],mid+1,r,ql,qr);
        sum.fi+=tmp.fi,sum.se+=tmp.se;
 
    }
    return sum;
}
void update(int &node,ll l,ll r,ll pos){
//    cout<<l<<" "<<r<<" "<<tree[node].fi<<" "<<tree[node].se<<endl;
    if(node==0) node=++cnt;
    // 引用
    if(l==r){
        tree[node].fi+=l;
        tree[node].se++;
//        cout<<l<<" "<<tree[node].fi<<" "<<tree[node].se<<endl;
        return ;
    }
    ll mid=(l+r)/2;
    if(mid>=pos){
        update(lson[node],l,mid,pos);
    }else{
        update(rson[node],mid+1,r,pos);
    }
    tree[node].fi=tree[lson[node]].fi+tree[rson[node]].fi;
    tree[node].se=tree[lson[node]].se+tree[rson[node]].se;
//    cout<<l<<" "<<r<<" "<<tree[node].fi<<" "<<tree[node].se<<endl;
 
}
int fd(int x,int i){
    if(x>mid)   p2.push(x);
    else        p1.push(x);
    if(i%2==0)//保证偶数情况 p1.size()=p2.size()
    {
        while(p1.size()>p2.size())
        {
            p2.push(p1.top());
            p1.pop();
        }
        while(p2.size()>p1.size())
        {
            p1.push(p2.top());
            p2.pop();
        }
    }
    else//保证奇数情况 p1.size()+1=p2.size()
    {
        while(p1.size()<p2.size()+1)
        {
            p1.push(p2.top());
            p2.pop();
        }
        while(p1.size()>p2.size()+1)
        {
            p2.push(p1.top());
            p1.pop();
        }
    }
    mid=p1.top();
    return mid;
}
signed main(){
//    cout<<mid<<endl;
//    cout<<tree[1].se<<endl;
    scanf("%lld",&n);
    int geng=1;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        a[i]+=1e9+1e6;
        a[i]-=(i-1);
        mid=fd(a[i],i);
        update(geng,1,2e9+1e6,a[i]);
        pair<ll,ll> tmp1=query(geng,1,2e9+1e6,1,mid);
        pair<ll,ll> tmp2=query(geng,1,2e9+1e6,mid+1,2e9+1e6);
        ll ans=0;
        ans+=tmp1.se*mid-tmp1.fi+tmp2.fi-tmp2.se*mid;
        printf("%lld\n",ans);
    }
    return 0;
}

代码2

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int,vector<int>,less<int> > p1;
priority_queue<int,vector<int>,greater<int> > p2;
int n,x,mid=-1e9-1e6;
long long cnt1=0,cnt2=0,sum1=0,sum2=0;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        x-=(i-1);
        if(x>mid){
            cnt2++;
            sum2+=x;
            p2.push(x);
        }else{
            cnt1++;
            sum1+=x;
            p1.push(x);
        }
        if(i%2==0)//保证偶数情况 p1.size()=p2.size()
        {
            while(p1.size()>p2.size())
            {
                cnt1--;
                cnt2++;
                sum1-=p1.top();
                sum2+=p1.top();
                p2.push(p1.top());
                p1.pop();
            }
            while(p2.size()>p1.size())
            {
                cnt1++;
                cnt2--;
                sum1+=p2.top();
                sum2-=p2.top();
                p1.push(p2.top());
                p2.pop();
            }
        }
        else//保证奇数情况 p1.size()=p2.size()+1
        {
            while(p1.size()<p2.size()+1)
            {
                cnt1++;
                cnt2--;
                sum1+=p2.top();
                sum2-=p2.top();
                p1.push(p2.top());
                p2.pop();
            }
            while(p1.size()>p2.size()+1)
            {
                cnt1--;
                cnt2++;
                sum1-=p1.top();
                sum2+=p1.top();
                p2.push(p1.top());
                p1.pop();
            }
        }
        mid=p1.top();
        long long  pr=cnt1*mid-sum1+sum2-cnt2*mid;
        printf("%lld\n",pr);
    }
    return 0;
}


上一篇:算法——归并排序-Java


下一篇:C/C++备忘录(二):指针基础