原题链接:F - Absolute Minima (atcoder.jp)
题目大意:给定一个函数f(x)=f(x)+|x-a|+b初始为0,然后输入q个查询,查询有二种操作,如果输入1,再输入二个数为a和b,更新f(x)。如果输入2,就输出f(x)的最小值以及对应的x。
思路:观察函数表达式,可以发现输入1的时候,b这个值并不会产生后续的影响。所以可以直接使用sum记录b的总和。对于a来讨论,例如如果查询为1 2 3,1 4 5,1 6 4,那么f(x)=|x-2|+|x-4|+|x-6|+12,那么如何取得最小值呢?可以从绝对值的几何意义来看,|x-2|代表x到2的距离,那么|x-2|+|x-4|+|x-6|就是x到2,4,6这些点的距离之和,如果x==2,那么f(x)=18,如果x==4,那么f(x)=16,如果x==6,那么f(x)=18,可以看出来,在中位数的时候可以取得最小的f(x)。因为会不断的加入数据,那么如何动态的维护中位数呢?可以使用对顶堆,开一个从小到大排序的堆和一个从大到小排序的堆。将二个堆拼接使用就可以动态的查找中位数了,从大到小排序的堆的堆顶就是要查找的中位数。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<ll,ll> pii;
const int N=1e6+10;
priority_queue<ll,vector<ll>,greater<ll>> p;//从小到大排序的堆
priority_queue<ll> q;//从大到小排序的堆
int main()
{
ios::sync_with_stdio(NULL);
cin.tie(0),cout.tie(0);
ll t;cin>>t;
ll sum=0,ans1=0,ans2=0;
while(t--)
{
ll x,y,z;cin>>x;
if(x==1)
{
cin>>y>>z;
sum+=z;//b的总和
q.push(y);//先把数据放到从大到小排序的堆
ans1+=y;
if(q.size()-1>p.size())//如果个数为偶数那么q和p的大小相同,如果个数为奇数那么q比p大一个
{
ll k=q.top();//将q中的最大值插入p中
p.push(k);
q.pop();
ans1-=k;
ans2+=k;
}
if(p.size()&&q.top()>p.top())//因为会不断的插入数据,那么就可能发生q的最大值大于p的最小值
{
ll a=q.top(),b=p.top();
q.pop(),p.pop();
q.push(b),p.push(a);
ans1=ans1+b-a;ans2=ans2-b+a;
}
}
else
{
ll k=q.top();
ll ans=k*q.size()-ans1+ans2-k*p.size()+sum;//可以这样理解
//0 1 4 6 7 8,0不在讨论范围内只是方便理解,对于这样的数据来说,中位数就是6,那么为了快速的
//求出其他数与中位数的差值,就可以用q.size()*k-ans1来表示q里面的差值和,也就是3*6-1+4+6,这就是
// 1 4到6的差值和,对于在p里面的数据,那么就是用ans2-k*p.size(),也就是7+8-6*2.
cout<<k<<' '<<ans<<endl;
}
}
return 0;
}