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
#define int long long
#define ll long long
#define fi first
#define se second
using namespace std;
const int maxn=2e6+5;
priority_queue,less > p1;
priority_queue,greater > p2;
int n,k,a[100000+5];
int mid=-1e9-1e6,cnt=1;
pair tree[maxn];
int lson[maxn],rson[maxn];
pair 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 sum={0,0};
if(mid>=ql) {
pair tmp=query(lson[node],l,mid,ql,qr);
sum.fi+=tmp.fi,sum.se+=tmp.se;
}
if(mid 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<=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<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)
{
p2.push(p1.top());
p1.pop();
}
}
mid=p1.top();
return mid;
}
signed main(){
// cout< tmp1=query(geng,1,2e9+1e6,1,mid);
pair 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
#include
#include
#include
using namespace std;
priority_queue,less > p1;
priority_queue,greater > 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-=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;
}