2 条题解

  • 1
    @ 2022-9-11 22:16:36

    我们将一个人的成绩对应到二维空间中的位置向量,pi=(x,y)\vec p_i=(x,y),一种排名的计算步骤被抽象为

    1. 确定一个法向量v\vec v , 这个法向量需要满足这些条件:
    • vv>0\vec v \cdot \vec v > 0
    • v\vec v指向第一象限(v[0],v[1]0\vec v[0],\vec v[1]\ge 0)
    1. $\text{GPA}_i=v[0]p[0]+v[1]p[1]=\vec v \cdot \vec p_i$,随后就可以得到小林排名。

    为什么这里不需要限制v[0]+v[1]=1v[0]+v[1] = 1呢?这是因为只要v[0]+v[1]>0v[0]+v[1] > 0,计算出来的GPA只是等比例放大缩小,不影响排名。

    设小林的位置向量是Q\vec Q,利用v\vec v当作方向向量的法向量(notice that!),可以确定一个过Q\vec Q点的直线L(v)\mathbb L(\vec v)(关于v\vec v的函数)。

    虽然L(v)\mathbb L(\vec v)千变万化,但是我们并不需要考虑那么多的情况,实际上我们只需要考虑:

    • L(v)\mathbb L(\vec v)经过包括QQ点的至少两个点
    • v=x or y\vec v=x \text{ or } y

    rank(L)\text{rank}(\mathbb L)表示如果确定直线是L\mathbb L,小林的排名是多少,我们发现使得小林排名最高的L\mathbb L中,一定至少有一个L0\mathbb L_0经过了包括QQ点的至少两个点,即"临界条件"(使得rank(L)\text{rank}(\mathbb L)变化)。

    而还有两个临界条件,分别就是x,yx,y

    此时O(n2)O(n^2)算法就有了,枚举一个点RR,法向量就是找到个n, s.t. nRQ\vec n ,\text{ s.t. }\vec n\perp \vec {RQ} (或者n-\vec n),然后计算所有人的GPA得到小林的排名。记得把v=(1,0),(0,1)\vec v=(1,0),(0,1)这几种情况考虑进去。

    其实有个O(n)O(n)做法,在这个基础上优化一下而已。你注意到,在QQ点右上和左下的点不需要管(Q对他们有绝对劣势或者绝对优势),只需要考虑左上和右下的点。而左上和右下的点对答案的贡献是关于直线的斜率kk的分段函数ffhh,分别把ffgg计算出来之后,再O(n)O(n)枚举斜率,利用双指针找到f(p)f(p)g(q)g(q)统计答案即可。

    • 0
      @ 2025-8-13 18:19:24

      提供一个 O(nlogn)O(n\log n) 的做法

      令第 ii 个人的两部分成绩分别为 aia_ibib_i,小林则为 a1a_1b1b_1

      c1=cc_1=c,则显然有 c2=1cc_2=1-c,这样我们可以将两个权重简化为一个

      排名最高 \Leftrightarrow 小林最终成绩比尽可能多的人高(或等于)

      若小林成绩高于或等于第 ii 个人:

      $$a_1c+b_1(1-c)\geq a_ic+b_i(1-c) \\ (a_1-b_1+b_i-a_i)c\ge b_i-b_1 \\ \left\{\begin{matrix} c\ge \frac{b_i-b_1}{a_1-b_1+b_i-a_i} & a_1-b_1+b_i-a_i>0\\ c\le \frac{b_i-b_1}{a_1-b_1+b_i-a_i} & a_1-b_1+b_i-a_i<0 \\ b_i-b_1\leq 0 & a_1-b_1+b_i-a_i=0 \end{matrix}\right. $$

      第三种情况(等于零)特判并排除,其余第 ii 个人相当于提供一个值 vi=bib1a1b1+biaiv_i = \frac{b_i-b_1}{a_1-b_1+b_i-a_i},我们所决定的 cc 大于等于或小于等于 viv_i 便可获得 11 点价值,并试图令总价值最高。

      因此我们将 viv_i 排序后暴力枚举 cc 处于每个间隔并计算答案即可(枚举 cc 处于每个点同样可行)

      代码采用分数排序

      #include<bits/stdc++.h>
      using namespace std;
      const int inf=0x3f3f3f3f;
      int n,m;
      int x,y;
      int a[5060],b[5060];
      struct node
      {
      	int p,q;
      	bool f;
      };
      node v[5060];
      int vl=0;
      bool cmp(node x,node y)
      {
      	if(x.p*y.q==y.p*x.q) return x.f>y.f;//避免多个值相同对结果计算的影响 
      	else return x.p*y.q<y.p*x.q;
      }
      int l1[5060],r0[5060];
      int sum=0;
      int main()
      {
      	scanf("%d",&n);
      	m=n;
      	scanf("%d%d",&x,&y);
      	n--;
      	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
      	for(int i=1;i<=n;i++)
      	{
      		int p,q;
      		p=b[i]-y;
      		q=x-y+b[i]-a[i];
      		if(q>0) v[++vl]=node{p,q,1};
      		else if(q<0) v[++vl]=node{p,q,0};
      		else
      		{
      			if(p<=0) sum++;//特判 
      		}
      	}
      	n=vl;
      	for(int i=1;i<=n;i++)
      	{
      		if(v[i].q<0) v[i].q=-v[i].q,v[i].p=-v[i].p;
      	}
      	sort(v+1,v+n+1,cmp);
      	l1[0]=0; r0[n+1]=0;
      	for(int i=1;i<=n;i++)
      	{
      		if(v[i].f) l1[i]=l1[i-1]+1;
      		else l1[i]=l1[i-1];
      	}
      	for(int i=n;i>=1;i--)
      	{
      		if(!v[i].f) r0[i]=r0[i+1]+1;
      		else r0[i]=r0[i+1];
      	}
      	int ans=0;
      	v[0].p=-inf;  v[0].q=1;
      	v[n+1].p=inf; v[n+1].q=1;
      	for(int i=0;i<=n;i++) //枚举间隔 
      	{
      		if(v[i+1].p<0||v[i].p>v[i].q) continue;
      		ans=max(ans,l1[i]+r0[i+1]);
      	}
      	/* 
      	for(int i=1;i<=n;i++) //枚举点 
      	{
      		if(v[i].p<0||v[i].p>v[i].q) continue;
      		ans=max(ans,l1[i]+r0[i]);
      	}
      	*/
      	printf("%d\n",m-(ans+sum));
      	return 0;
      }
      
      • 1

      信息

      ID
      159
      时间
      1000ms
      内存
      256MiB
      难度
      10
      标签
      (无)
      递交数
      666
      已通过
      19
      上传者