题意:
Bessie考了N科(N≤50000),每科的得分为Ti,满分为Pi(样例似乎是五科红灯……)。
现在Bessie的老师要给她们统计最终得分,方法 是先算出每一科得分的百分比,去除D个百分比最低的科目,然后剩下科目的∑T / ∑P就是最终得分的百分比。
很幸运,没有人有两科的得分比一样。Bessie的数学很牛X,她马上就发现她的老师在坑爹,因为有时候可以通过去除不同的科 目来获得更高的分数。
现在Bessie想知道,对于哪些D值,她可以通过去除与老师不同的D科,从而获得更高的分数呢?
(摘自http://www.cnblogs.com/perseawe/archive/2012/05/03/01fsgh.html)
做法:
假设现在取了i个百分比靠前的科目,当前的综合百分比是k。
如果能把选的科目p替换成一个没选的科目q使得综合百分比提高 ,根据01分数规划的思想,显然 p.T-k*p.P<q.T-k*q.P 。
于是就可以在选的科目中找一个科目 p 使得 p.T-k*p.P最小,再在没选的科目中找一个科目 q 使 q.T-k*q.P 最大 。
然后发现 T-k*P 就是向量的点积 (1,-k)*(T,P) ,就可以用平衡树维护两个凸壳来找最大值和最小值。
注意到:排序后 选的科目越多,综合百分比越低,因此k是单调的,于是便不需要在平衡树里二分查找了。
网上题解说可以用单调栈,但是总感觉有问题,还是用了平衡树。
我依旧是把上凸壳转化成下凸壳用同一个函数来做。
#include<cstdio> #include<map> #include<algorithm> #define X first #define Y second using namespace std; typedef long long ll; typedef map<int,int>::iterator itr; const int N=50005; int n; double k[N],fmax[N],fmin[N]; int ans[N]; struct P { int x,y; P operator-(const P &t)const { return {x-t.x,y-t.y}; } int operator^(P t) { return x*t.y-y*t.x; } }a[N]; bool cmp(P a,P b) { return 1.0*a.y/a.x>1.0*b.y/b.x; } double dot(itr it,double x,double y) { return it->X*x+it->Y*y; } ll cross(itr l,P p2,itr r) { P p1={l->X,l->Y},p3={r->X,r->Y}; return (p2-p1)^(p3-p2); } struct hull { map<int,int>s; itr mid,l,r,p1,p2; void init() { s.clear(); } itr pre(itr it) { if (it==s.end()) return it; if (it==s.begin()) return s.end(); return --it; } itr suc(itr it) { if (it==s.end()) return it; return ++it; } bool inside(P p) { if (s.empty()) return 0; r=s.lower_bound(p.x); if (r==s.end()) return 0; if (r->X==p.x) return p.y>=r->Y; if (r==s.begin()) return 0; l=r; l--; return cross(l,p,r)<=0; } void add(P p) { if (inside(p)) return; s[p.x]=p.y; mid=s.find(p.x); p1=suc(mid); p2=suc(p1); while(p1!=s.end()&&p2!=s.end()&&cross(mid,{p1->X,p1->Y},p2)<=0) s.erase(p1),p1=p2,p2=suc(p2); p1=pre(mid); p2=pre(p1); while(p1!=s.end()&&p2!=s.end()&&cross(mid,{p1->X,p1->Y},p2)>=0) s.erase(p1),p1=p2,p2=pre(p2); } double get(double x,double y) { r=s.end(); r--; l=pre(r); while(l!=s.end()&&dot(l,x,y)<=dot(r,x,y)) s.erase(r),r=l,l=pre(l); return dot(r,x,y); } }G; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d%d",&a[i].y,&a[i].x); sort(a+1,a+n+1,cmp); double sumT=0,sumP=0; for (int i=1;i<=n;i++) sumT+=a[i].y,sumP+=a[i].x,k[i]=sumT/sumP; G.init(); for (int i=1;i<=n;i++) { G.add(a[i]); fmin[i]=G.get(-k[i],1); } G.init(); for (int i=n;i>=2;i--) { G.add({a[i].x,-a[i].y}); fmax[i-1]=-G.get(k[i-1],1); } int cnt=0; for (int i=1;i<n;i++) if (fmin[i]<fmax[i]) ans[++cnt]=n-i; printf("%d\n",cnt); for (int i=cnt;i>=1;i--) printf("%d\n",ans[i]); }