Exam in BerSU (hard version)
这道题是属于codeforce上的一道题,说明不会用到一些特别难写的数据结构,这道题是要你维护一个关于整数的多重集合,然后给你一个背包容量,查询用这个背包最多可以装多少个该集合里的数,显然,每次取走集合中最小的那个数,我们的背包才能装的更多。所以该集合要维护成有序集。
我们对该有序集要实行两个功能:1.插入新元素。2.查询前n小的数之和。
这道题用splay的话复杂度是插入+查询前n小的数=logn,但是splay太难写了,想了想,用树状数组加二分就可以实现这个功能,复杂度logn^2,但是代码要简洁的多!
#include<cstring>
#include<stdio.h>
#include<cmath>
#include<map>
#include<vector>
#include<set>
#include<sstream>
#include<queue>
#include<algorithm>
#include<stack>
#include<cstdlib>
#include<deque>
#include<bitset>
#include<iostream>
#define FIN freopen("D://debug//in.txt","r",stdin);
#define FOUT freopen("D://debug//out.txt","w",stdout);
#define FDATA freopen("D://debug//data.txt","w",stdout);
using namespace std;
typedef long long int ll;
const int maxn=2e5+5;
int n,m;
int a[maxn],b[maxn];
int a2[maxn];ll b2[maxn];
void add1(int d,int v){
while(d<=m){
a2[d]+=v;d+=d&-d;
}
}
void add2(int d,int v){
while(d<=m){
b2[d]+=v;d+=d&-d;
}
}
int sum1(int d){
int ans=0;
while(d){
ans+=a2[d];d-=d&-d;
}
return ans;
}
ll sum2(int d){
ll ans=0;
while(d){
ans+=b2[d];d-=d&-d;
}
return ans;
}
int h(int M){
int low=1,hign=m;
while(low<=hign){
int mid=(low+hign)/2;
if(sum2(mid)>=M)hign=mid-1;
else low=mid+1;
}
int k=b[low-1],cnt=sum1(low);
ll ans=sum2(low);
return (ans-M)%k==0?cnt-(ans-M)/k:cnt-(ans-M)/k-1;
}
int f(int x){
return (lower_bound(b,b+m,x)-b)+1;
}
int main(){
//FIN
int v;
cin>>n>>v;m=0;
for(int i=1;i<=n;i++)cin>>a[i],b[m++]=a[i];
sort(b,b+m);
m=unique(b,b+m)-b;
ll sum=0;
for(int i=1;i<=n;i++){
sum+=a[i];
if(sum<=v){
printf("0%c",i==n?'\n':' ');
}
else printf("%d%c",i-1-h(v-a[i]),i==n?'\n':' ');
int pos=f(a[i]);
add1(pos,1);
add2(pos,a[i]);
}
}
我想的测试数据:
7 100
1 1 1 99 1 99 96