1007 Find the answer
给w数组,求
w
[
1
]
−
w
[
i
]
w[1]-w[i]
w[1]−w[i]需要去掉几个数才能满足
s
u
m
sum
sum小于等于m
思路
最初开始做的时候想的太简单了,那时候用两个优先队列维护去掉的数和没去掉的数,每次都从大到小去数,然后再把多去掉的数还回去,这样做法是没错,但是对于类似
100 100
1 1 1 1 1 1 100 1 1 1 1 1 100 1 1 1 1
这样的数据的时候,复杂度是
O
(
n
2
)
O(n^2)
O(n2)显然是不行的
正解就是用权值线段树来维护总和,和离散化后的数目,按照数组顺序update,这样可以保证有序,也就是求每一次的sum-m,需要最少去掉多少个数,具体还是看代码吧
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define lson node<<1
#define rson node<<1|1
using namespace std;
typedef long long ll;
const int maxn = 2e5+20;
const ll inf=1e18-1;
ll sum[4*maxn],cnt[4*maxn];
ll w[maxn];
int n,m,N;
vector<ll> ls;
void init()
{
memset(sum,0,sizeof(sum));
memset(cnt,0,sizeof(cnt));
ls.clear();
}
int getid(ll x) //离散化
{
return lower_bound(ls.begin(),ls.end(),x)-ls.begin()+1;
}
void update(int node,int l,int r,int k,int id)
{
if(l==r&&id==l)
{
sum[node]+=k;
cnt[node]++;
return ;
}
int mid=(l+r)/2;
if(id<=mid)
{
update(lson,l,mid,k,id);
}
else
{
update(rson,mid+1,r,k,id);
}
sum[node]=sum[lson]+sum[rson]; //记录总和
cnt[node]=cnt[lson]+cnt[rson]; //记录总数量
}
int query(int node,int l,int r,ll k)
{
if(k<=0)
return 0;
if(l==r)
{
if(k%ls[l-1]==0) //只剩一种数的时候
return k/ls[l-1];
else
return k/ls[l-1]+1;
}
int mid=(l+r)/2;
int ans=0;
if(k>=sum[rson]) //因为右子树都是最大的树,所以尽可能取右子树里的值
{
ans+=cnt[rson];
ans+=query(lson,l,mid,k-sum[rson]);
}
else{
ans+=query(rson,mid+1,r,k);
}
return ans;
}
int main(){
int t;
cin>>t;
while(t--)
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&w[i]);
ls.push_back(w[i]);
}
sort(ls.begin(),ls.end());
ls.erase(unique(ls.begin(), ls.end()), ls.end());
N=ls.size();
ll summ=0;
for(int i=1;i<=n;i++)
{
summ+=w[i];
printf("%d ",query(1,1,N,summ-m));
update(1,1,N,w[i],getid(w[i]));
}
printf("\n");
}
}