题意:
一个人参加了 n n n 场考试,第 i i i 场满分为 p i p_i pi,其得分为 t i t_i ti。现在要删去其中 d d d 次考试的成绩,用剩下的总得分除以剩下的满分之和,作为其最终成绩。问对于哪些 d d d 而言,删除得分比(即 t i p i \frac{t_i}{p_i} piti )最小的 d d d 场得到的最终成绩不是最优的
范围&性质: 1 ≤ n ≤ 5 × 1 0 4 , 1 ≤ p i , t i ≤ 4 × 1 0 4 1\le n\le 5\times 10^4,1\le p_i,t_i\le 4\times 10^4 1≤n≤5×104,1≤pi,ti≤4×104
分析:
首先这个式子长得很分数规划,但是按照正常分数规划的做法,复杂度是 O ( n 2 log n ) O(n^2\log n) O(n2logn)的,直接去世
我们发现其实并不需要求出分数规划的答案,我们只需要知道选的数是不是最小的 d d d 个,那就按照正常分数规划的套路,求出所有的 t − r a t e × p t-rate\times p t−rate×p ,如果未选的最大值大于已选的最小值,那么该方案一定不是最优,所以问题就转化成了求前缀序列的最小值和后缀序列的最大值
由于 t − r a t e × p t-rate\times p t−rate×p 这种形式很斜率优化,且 r a t e rate rate 是单调的,能成为决策点的 p i p_i pi 也是单调的
- 证明:
对于 t i − r a t e × p i < t j − r a t e × p j t_i-rate\times p_i<t_j-rate\times p_j ti−rate×pi<tj−rate×pj 且 i < j i<j i<j
由于 t i p i ≤ t j p j \frac{t_i}{p_i}\le \frac{t_j}{p_j} piti≤pjtj 所以当 p j > p i p_j>p_i pj>pi 时存在 t j − t i p j − p i > r a t e \frac{t_j-t_i}{p_j-p_i}>rate pj−pitj−ti>rate 即 j j j 可以成为决策点
然后我们就可以按照正常的斜率优化的做法解题,对于求已选的最小值可以用单调队列维护一个递增的序列,对于未选的最大值可以用单调栈维护递减的值
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 5e4+5;
int n,h,t,top,ans;
int q[maxn],sumt[maxn],sump[maxn],pos[maxn];
double hig[maxn],low[maxn];
struct node
{
int p,t;
bool operator<(const node &b)const
{
return p*b.t<b.p*t;
}
}a[maxn];
int up(int x,int y)
{
return (a[x].p-a[y].p);
}
int down(int x,int y)
{
return (a[x].t-a[y].t);
}
void work()
{
n=read();
for(int i=1;i<=n;i++) a[i].t=read(),a[i].p=read();
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) sumt[i]=sumt[i-1]+a[i].t,sump[i]=sump[i-1]+a[i].p;
h=1;t=0;
for(int i=1;i<=n;i++)
{
while(h<=t&&a[i].p>=a[q[t]].p) t--;
while(h<t&&(long long)up(q[t-1],q[t])*down(q[t],i)>(long long)up(q[t],i)*down(q[t-1],q[t])) t--;
q[++t]=i;
while(h<t&&(long long)down(q[h],q[h+1])*sump[i]>(long long)up(q[h],q[h+1])*sumt[i]) h++;
low[i]=a[q[h]].t-(double)sumt[i]/sump[i]*a[q[h]].p;
}
top=0;
for(int i=n;i>=1;i--)
{
while(top&&a[i].p<=a[q[top]].p) top--;
while(top>1&&(long long)down(i,q[top])*up(q[top],q[top-1])>(long long)up(i,q[top])*down(q[top],q[top-1])) top--;
q[++top]=i;
while(top>1&&(long long)down(q[top],q[top-1])*sump[i-1]<=(long long)up(q[top],q[top-1])*sumt[i-1]) top--;
hig[i]=a[q[top]].t-(double)sumt[i-1]/sump[i-1]*a[q[top]].p;
}
for(int i=1;i<n;i++) if(hig[i+1]>low[i]) pos[++ans]=n-i;
printf("%d\n",ans);
for(int i=ans;i>=1;i--) printf("%d\n",pos[i]);
}
}
int main()
{
zzc::work();
return 0;
}