[USACO07JAN]Cow School

题目链接:[USACO07JAN]Cow School


一开始还以为是一道分数规划,后来发现自己看错题了,

然后成功入坑

题目是要求先按照$t_i/p_i$从小到大排序,然后删除前$d$个后求出剩下的$\frac{\sum^{n-d+1}_{i=1}t_i}{\sum^{n-d+1}_{i=1}p_i}$,如果当前的删除方法不是最优的(即能删除其它$d$个数使原式的值变大的话),则输出这个$d$

首先排序自不必说,排完序之后基础的式子的值也就求出来了

那么问题就在于如何判断当前的删除方法是不是最优的

 

为了方便我们将数据从大到小排序

我们假设当前取了前$k$个最大的

并设$T=t_1+t_2+\cdots+t_k,P=p_1+p_2+\cdots+p_k$

那么题目就是求是否存在$i,j$,满足$\frac{T-t_i+t_j}{P-p_i+p_j}>\frac{T}{P}$(其中$i\in \lbrace1,2,\cdots,k \rbrace,j\notin \lbrace1,2,\cdots,k \rbrace$)

将式子变形得$P*t_i-T*p_i<P*t_j-T*t_i$

所以我们只要找出$P*t_i-T*p_i$的$min$,以及$P*t_j-T*p_j$的$max$

这时我们再来算一算时间复杂度

我们在枚举前$k$个最大的数时需要$O(n)$的时间,

因此我们在处理最大值与最小值时只能用$O(logn)$的时间复杂度

但由于我们的$与T_i与P_i$是已经排好序的,所以我们可以用二分来处理这个最大值与最小值

感觉洛谷上这题给黑题是不是太高看了(逃

 

 1 #include<iostream>
 2 #include<string>
 3 #include<string.h>
 4 #include<stdio.h>
 5 #include<algorithm>
 6 #include<vector>
 7 #include<queue>
 8 #include<map>
 9 using namespace std;
10 const long long inf=(long long)1e18;
11 struct node{
12     long long t,p;
13 }a[50050],b[50050];
14 int n,ans[50050];
15 long long mind[50050],maxd[50050];
16 
17 bool cmp(node x,node y)
18 {
19     return x.t*y.p>x.p*y.t;
20 }
21 
22 void getmin(int l,int r,int pl,int pr)
23 {
24     int i,mid=(l+r)>>1,pos;
25     for (i=pl;i<=mid&&i<=pr;i++)
26     {
27         long long tmp=a[i].t*b[mid].p-a[i].p*b[mid].t;
28         if (tmp<mind[mid]) {mind[mid]=tmp;pos=i;}
29     }
30     if (l<mid) getmin(l,mid-1,pl,pos);
31     if (r>mid) getmin(mid+1,r,pos,pr);
32 }
33 
34 void getmax(int l,int r,int pl,int pr)
35 {
36     int i,pos,mid=(l+r)>>1;
37     for (i=pr;i>mid&&i>=pl;i--)
38     {
39         long long tmp=a[i].t*b[mid].p-a[i].p*b[mid].t;
40         if (tmp>maxd[mid]) {maxd[mid]=tmp;pos=i;}
41     }
42     if (l<mid) getmax(l,mid-1,pl,pos);
43     if (r>mid) getmax(mid+1,r,pos,pr);
44 }
45 
46 int main()
47 {
48     scanf("%d",&n);
49     int i;
50     for (i=1;i<=n;i++) scanf("%lld%lld",&a[i].t,&a[i].p);
51     sort(a+1,a+1+n,cmp);
52     b[0].t=0;b[0].p=0;
53     for (i=1;i<=n;i++)
54     {
55         b[i].t=b[i-1].t+a[i].t;
56         b[i].p=b[i-1].p+a[i].p;
57     }
58     for (i=1;i<=n;i++) {mind[i]=inf;maxd[i]=-inf;}
59     getmin(1,n-1,1,n);getmax(1,n-1,1,n);
60     int pos=0;
61     for (i=1;i<n;i++) if (mind[i]<maxd[i]) ans[++pos]=n-i;
62     printf("%d\n",pos);
63     for (i=pos;i>=1;i--) printf("%d\n",ans[i]);
64     return 0;
65 }

 

转载于:https://www.cnblogs.com/encodetalker/p/9773127.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值