猴子拆房 题解
题目描述
输入
输出
样例输入
【样例输入1】 2 2 3 4 5 【样例输入2】 3 2 4 2 5 1 3 【样例输入3】 6 3 5 3 4 1 7 1 7 4 2 4 1
样例输出
【样例输出1】 3 【样例输出2】 0 【样例输出3】 10
数据范围限制
提示
这个是我的,是我的QWQ,我没有转载,只是把以前的博客搬运过来了。
然后就是因为markdown丢了,所以我截图:
不过代码还在OJ上存着,你们放心:
#include<cstdio>
#include<algorithm>
#define ll long long
#define INF 1e12
using namespace std;
ll n;
struct node{
friend bool operator <(const node &x,const node &y){ // 懒得改了
return x.v<y.v;
}
friend bool operator >(const node &x,const node &y){
return x.v>y.v;
}
ll h,v; // 小根堆 大根堆
} a[200010],b[200010],t1[200010],t2[200010];
ll num[200010];
ll cnt1,cnt2;
ll big,sum,mn=INF;
ll spend[200010];
ll s[200010];
// 下面是手写堆
// 下面的肯定对了!
void insert1(node k){
t1[++cnt1]=k;
ll x=cnt1;
while(x>1 && t1[x/2]>t1[x]){
swap(t1[x/2],t1[x]);
x/=2;
}
}
void pop1(){
swap(t1[1],t1[cnt1]);
cnt1--;
ll x=1;
while(2*x<=cnt1&&((2*x+1<=cnt1 && t1[x*2+1]<t1[x]) || t1[x*2]<t1[x])){
if(2*x+1>cnt1){
swap(t1[x*2],t1[x]);
x*=2;
}
else if(t1[x*2+1]<t1[x*2]){
swap(t1[x*2+1],t1[x]);
x=x*2+1;
}
else{
swap(t1[x*2],t1[x]);
x*=2;
}
}
}
void insert2(node k){
t2[++cnt2]=k;
ll x = cnt2;
while(x>1 && t2[x/2]<t2[x]){
swap(t2[x/2],t2[x]);
x/=2;
}
}
void pop2(){
swap(t2[1],t2[cnt2]);
cnt2--;
ll x=1;
while(2*x<=cnt2&&((2*x+1<=cnt2 && t2[x*2+1]>t2[x]) || t2[x*2]>t2[x])){
if(2*x+1>cnt2){
swap(t2[x*2],t2[x]);
x*=2;
}
else if(t2[x*2+1]>t2[x*2]){
swap(t2[x*2+1],t2[x]);
x=x*2+1;
}
else{
swap(t2[x*2],t2[x]);
x*=2;
}
}
}
void solve(ll l,ll r){
if(l==r) return;
ll mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
ll pos1=l,pos2=mid+1;
for(ll i=l;i<=r;i++){
if(pos2>r||(pos1<=mid && a[pos1].h<a[pos2].h)){
b[i]=a[pos1++];
}
else if(pos2<=r){
b[i]=a[pos2++];
}
}
for(ll i=l;i<=r;i++){
a[i]=b[i];
}
}
// 上面的肯定对了!
// 代码主要部分
void run(ll h){
ll k=(n-s[h])-(num[h]-1); // 最少拆多少个房子
while(k<cnt2 && cnt2){ // 多了
insert1(t2[1]);
sum-=t2[1].v;
pop2();
}
while(cnt2<k){ // 不够
if(!cnt1){
return;
}
insert2(t1[1]);
sum+=t1[1].v;
pop1();
}
big-=spend[h];
mn=min(mn,sum+big);
}
int main(){
freopen("house.in","r",stdin);
freopen("house.out","w",stdout);
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
scanf("%lld %lld",&a[i].h,&a[i].v);
num[a[i].h]++;
spend[a[i].h]+=a[i].v;
}
solve(1,n);
for(ll i=100000;i>=1;i--){
s[i]=s[i+1]+num[i];
big+=spend[i];
}
ll i=1;
for(ll h=1;h<=100000;h++){
if(num[h]){
while(i<=n){
if(a[i].h>=h) break;
insert2(a[i]);
sum+=a[i].v;
insert1(t2[1]);
sum-=t2[1].v;
pop2();
i++;
}
run(h);
}
}
printf("%lld",mn);
}
另外再贴HLY大佬的题解一篇(单堆,不理解我的可以试着理解他的):
虽然格式有点……但是思路很新奇。
代码帮你们从OJ上copy过来了,码风和我的不太一样:
#include<bits/stdc++.h>
using namespace std;
int n,b[100001],t[100001],tt[100001],cnt,sum,h=1,p[1000001],f[100001],k[100001],g,ans=23407234;
struct st{
int h;
int c;
}a[100001];
void up(long long x)
{
p[++cnt]=x;
long long wz=cnt;
while(wz>1&&p[wz]>p[wz/2])
{
swap(p[wz],p[wz/2]);
wz/=2;
}
}
long long down()
{
long long maxn=p[1],wz=1;
p[1]=p[cnt--];
while(wz*2<=cnt&&p[wz]<p[wz*2]
||wz*2+1<=cnt&&p[wz]<p[wz*2+1])
{
long long mx=p[wz*2],k=wz*2;
if(wz*2+1<=cnt&&p[wz*2]<p[wz*2+1])
{
mx=p[wz*2+1],k=wz*2+1;
}
swap(p[wz],p[k]);
wz=k;
}
return maxn;
}
void hb(int x,int y,int p,int q)
{
int i=x,j=p,k=x;
st b[200001];
while(i<=y&&j<=q)
{
if(a[i].h<a[j].h) b[k].c=a[i].c,b[k++].h=a[i++].h;
else b[k].c=a[j].c,b[k++].h=a[j++].h;
}
while(i<=y) b[k].c=a[i].c,b[k++].h=a[i++].h;
while(j<=q) b[k].c=a[j].c,b[k++].h=a[j++].h;
for(int l=x;l<=q;l++)
{
a[l].h=b[l].h,a[l].c=b[l].c;
}
}
void gb(int l,int r)
{
if(l==r) return;
int mid=(l+r)/2;
gb(l,mid);
gb(mid+1,r);
hb(l,mid,mid+1,r);
}
int main()
{
freopen("house.in","r",stdin);
freopen("house.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].h,&a[i].c);
}
gb(1,n);
for(int i=1;i<=n;i++)
{
if(t[a[i].h]==0)
{
b[++b[0]]=a[i].h;
}
t[a[i].h]++;
tt[a[i].h]+=a[i].c;
sum+=a[i].c;
}
for(int i=1;i<=b[0];i++)
{
int uu=b[i],g1=0;
sum-=tt[uu];
f[i]=sum;
while(a[h].h<uu) g+=a[h].c,up(a[h++].c);
if(t[uu]<=cnt)
{
for(int j=1;j<=t[uu]-1;j++)
{
k[++k[0]]=down();
g1+=k[k[0]];
}
f[i]+=g-g1;
for(int j=1;j<=k[0];j++)
{
up(k[j]);
}
k[0]=0;
}
ans=min(ans,f[i]);
}
printf("%d",ans);
}
另外手动膜拜HLY大佬,这里贴上他的博客