正解……正解是二分W值,每次check的时候遍历一遍序列处理满足条件的矿产的前缀和个数和前缀和价值,然后对于每个询问直接用前缀和求得贡献。复杂度O((n+m)log(maxw))。
惨烈的数据结构学傻现场……真的,数据结构降智。之前运输计划那题我还想暴力树剖+线段树分治。
而对于一个数据结构学傻的人来说就不一样了。
读完题一分钟:“二分W,然后……这不主席树嘛!”
¿
n不超过20万,于是将读入的矿产质量离散化,主席树维护到每个位置时前缀质量个数的情况以及价值的情况。
二分W,然后check时对每个区间询问进行一次主席树上的查询,传回满足条件的二元组num个数和sum价值和,相乘贡献答案。
不开O2只有95pts,开O2AC。
就……就当练练主席树了。
二分时无论check传回的贡献比s大还是小都要更新记录的答案,等于s直接输出0,最后比较记录下来的两个答案哪个更优。
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int N=200010,M=20000010; int n,m,maxx,fir[N],lst[N]; int T[N],L[M],R[M],tot,num[M]; ll ans1,ans2,s,sum[M]; int read(){ char ch=getchar(); int val=0; for(;(ch<'0')||(ch>'9');ch=getchar()); for(;(ch>='0')&&(ch<='9');val=val*10+ch-'0',ch=getchar()); return val; } struct thing{ int w,v; }thi[N]; int c[N],cnt; struct node{ int num; ll sum; node(int x=0,ll y=0){ num=x,sum=y; } }; void build(int &p,int l,int r){ if(!p)p=++tot; if(l==r)return; int mid=(l+r)/2; build(L[p],l,mid); build(R[p],mid+1,r); } void ins(int &p,int p0,int l,int r,int pos,int val){ p=++tot; num[p]=num[p0]+1,sum[p]=sum[p0]+val; L[p]=L[p0],R[p]=R[p0]; if(l==r)return; int mid=(l+r)/2; if(pos<=mid)ins(L[p],L[p0],l,mid,pos,val); else ins(R[p],R[p0],mid+1,r,pos,val); } node query(int p,int p0,int l,int r,int pos){ if(pos<=l){ return node(num[p]-num[p0],sum[p]-sum[p0]); } int mid=(l+r)/2; if(pos>mid)return query(R[p],R[p0],mid+1,r,pos); else{ node val1=query(L[p],L[p0],l,mid,pos); node val2=query(R[p],R[p0],mid+1,r,pos); return node(val1.num+val2.num,val1.sum+val2.sum); } } ll work(int x){ ll val=0; for(int i=1;i<=m;i++){ node ans=query(T[lst[i]],T[fir[i]-1],1,200000,x); val+=ans.num*ans.sum; } return val; } int main() { // freopen("1.in","r",stdin); n=read(),m=read(); scanf("%lld",&s); build(T[0],1,200000); for(int i=1;i<=n;i++){ thi[i].w=read(),thi[i].v=read(); c[++cnt]=thi[i].w; maxx=max(maxx,thi[i].w); } sort(c+1,c+cnt+1); cnt=unique(c+1,c+cnt+1)-c-1; for(int i=1;i<=n;i++){ thi[i].w=lower_bound(c+1,c+cnt+1,thi[i].w)-c; ins(T[i],T[i-1],1,200000,thi[i].w,thi[i].v); } for(int i=1;i<=m;i++){ fir[i]=read(),lst[i]=read(); } int l=1,r=maxx; while(l<=r){ int mid=(l+r)/2; ll val1=work(mid); if(val1==s){ printf("0\n"); return 0; } if(val1>s){ ans1=val1; l=mid+1; } else{ ans2=val1; r=mid-1; } } if(ans1-s>s-ans2){ printf("%lld\n",s-ans2); } else printf("%lld\n",ans1-s); return 0; }
正解比较好写,不重写了。