COCI2014/2015 Contest#3 D

Description:

在三场考试中,有 n n n为选手,现在已知每位选手的第一场和第二场的分数,并且知道若选手 A A A的第一场和第二场的分数都严格大于选手 B B B,则 A A A的第三场的分数一定大于 B B B,以及每场的分数为0~650。求每位选手最后总分的最高排名和最低排名。
n ≤ 500000 n\le50 0000 n500000

Solution:

  • 模拟小数据,发现,满足题意的严格大于的选手 A A A的排名一定比 B B B高,小于同理,即 A A A的最高排名-1, B B B的最低排名+1。
  • 由于分数比较小,那么我们可以直接用二维前缀和来维护。
  • 但是第三场的分数最高为650。考虑 A A A最高排名,如果给了 A A A 650分,给大于 A A A的人650分,给其他人0分,可能有其他人总分大于等于 A A A的。
  • 其实不会大于,只会等于,因为既然那个人不大于 A A A,最多可能是有一场等于 A A A,有一场为650。
  • A A A那一场最低为0,也就是两人之前的分差最多为650。那么加上第三场后两人恰好相等。同理,考虑最低排名,给$A$0分,给其他人650分,也会有人和A相等。
  • 由于分数相同取高位,最高排名不用管,只需要在最低排名判断,如果 A A A有一场为650,那么排名就要减去另一场相等,这一场为0的人数。

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
templateinline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
templateinline bool chkmax(T &x,T y){return xinline void Rd(T &x){
    x=0;char c;int f=1;
    while((c=getchar())<48)if(c=='-')f=-1;
    do x=(x<<1)+(x<<3)+(c^48);
    while((c=getchar())>47);
    x*=f;
}
const int N=5e5+2,M=655;
 
int n;
int A[N],B[N];
int sum1[M][M],sum2[M][M];
 
int main(){
//  freopen("coci.in","r",stdin);
//  freopen("coci.out","w",stdout);
    Rd(n);
    REP(i,1,n) {
        Rd(A[i]),Rd(B[i]);
        sum1[A[i]][B[i]]++;
        sum2[A[i]][B[i]]++;
    }
     
    REP(i,0,M-5) REP(j,0,M-5){
        if(i>0) sum1[i][j]+=sum1[i-1][j];
        if(j>0) sum1[i][j]+=sum1[i][j-1];
        if(i>0 && j>0) sum1[i][j]-=sum1[i-1][j-1];
    }
     
    REP(i,1,n){
        int Mx=1+n-sum1[650][B[i]]-sum1[A[i]][650]+sum1[A[i]][B[i]];
        int Mn=n-((!A[i] || !B[i])?0:sum1[A[i]-1][B[i]-1]);
        if(A[i]==650) Mn-=sum2[0][B[i]];
        if(B[i]==650) Mn-=sum2[A[i]][0];
        printf("%d %d\n",Mx,Mn);
    }
    return 0;
}

你可能感兴趣的:(COCI)