跟风打道题
枚举最长的长度,那么当前的长度全部不砍肯定最优
长度比这个大的一定要砍,可以直接求和
如果数量没到总共的一半,就从比当前长度小的桌腿里砍掉一些来满足题设,可以用线段树维护
(用堆也行,移步jz大佬的博客)
#include<cstdio>
#include<algorithm>
#include<vector>
#define N 100000
#define LL long long
#define ls (x<<1)
#define rs (x<<1|1)
#define mid (l+r>>1)
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for(;!(c>='0'&&c<='9');c=nc()) if(c=='-') b=-1;
for(x=0;c>='0'&&c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
struct node{int v,l;}a[N+5];
vector<int>t[N+5];
int n,len,maxn;
LL tot,ans,sum[N*4+5],num[N*4+5],s[N+5];
void change(int x,int l,int r,int k){
if(l==r&&l==k){
sum[x]+=1ll*k;
++num[x];
return;
}
if(k<=mid) change(ls,l,mid,k);
else change(rs,mid+1,r,k);
sum[x]=sum[ls]+sum[rs],num[x]=num[ls]+num[rs];
}
LL query(int x,int l,int r,int k){
if(l==r) return 1ll*l*k;
if(k==num[ls]) return sum[ls];
if(k>num[ls]) return sum[ls]+query(rs,mid+1,r,k-num[ls]);
else return query(ls,l,mid,k);
}
int main(){
// freopen("init.in","r",stdin);
read(n);
for(int i=1;i<=n;++i){
read(a[i].l);
len=max(len,a[i].l);
}
for(int i=1;i<=n;++i){
read(a[i].v);
tot+=1ll*a[i].v,s[a[i].l]+=1ll*a[i].v;
t[a[i].l].push_back(a[i].v);
maxn=max(maxn,a[i].v);
}
int now=0;
ans=1e18;
for(int i=1;i<=len;++i)
if(t[i].size()){
now+=t[i].size(),tot-=s[i];
int ned=now-2*t[i].size()+1;
if(ned<=0) ans=min(ans,tot);
else ans=min(ans,tot+query(1,1,maxn,ned));
for(int j=0;j<t[i].size();++j) change(1,1,maxn,t[i][j]);
}
printf("%lld\n",ans);
return 0;
}