Soltuion [COCI 2014/2015 #3]COCI
题目大意:有(n)个人,给定他们的第一轮成绩和第二轮成绩,如果(A)第一轮成绩和第二轮成绩均大于(B)的,那么(A)的第三轮成绩大于等于(B)的第三轮成绩。求每个人可能的最小、最大排名
思维
分析:
首先最小排名等于成绩一定比他高的人数(+1),这个比较显然。最大排名等于(n)减去成绩一定不高于他的人数
先看最小排名,成绩一定比他高,满足以下两个条件之一(设当前这个人为(now),另一个人为(A),两次成绩为(X,Y))
(1.egin{cases}X_{now}<X_A \ Y_{now}<Y_Aend{cases})
(2.X_{now}+Y_{now}+650<X_A+Y_A)(哪怕你第三次阿克总分都没有另一个人第三次抱零高)被巨佬碾压
满足第二个的条件的一定包含在第一个条件里面了,当第二个条件成立时,不妨假设(X_{now} geq X_{A})
那么有(X_A-X_{now} leq0)
由第二个条件推出(Y_{now}+650-Y_{A}<X_A-X_{now})
由于(X,Yin[0,650]),不等号左边是个非负数,右边是个非正数,显然不成立。得证。
所以求最小排名只需要一个前缀和。
最大排名,成绩一定不高于他,满足以下两个条件之一
(1.egin{cases}X_{now}>X_A \ Y_{now}>Y_Aend{cases})
(2.X_{now}+Y_{now}geq X_A+Y_A+650)(哪怕你第三次抱零总分都不低于另一个人第三次阿克)巨佬碾压别人
容斥原理可以算,我们证明满足第二个条件,但是不在第一个条件里面的最多只有两个点。
仍假设第二个条件成立,假设(X_{now}leq X_A),那么有
(X_{now}-X_{A}geq Y_A+650-Y_{now})
(X_{now}-X_Aleq0)
左边是个非正数,右边是个非负数,成立时两侧都为(0)
有(egin{cases}Y_{now}=650\X_A=X_{now}\Y_A=0end{cases})
另一种情况同理,仍然前缀和来做
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn = 5e5 + 100,maxk = 650;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
struct node{int x,y;}val[maxn];
int n,tmp[maxk + 10][maxk + 10],pre[maxk + 10][maxk + 10],suf[maxk + 10][maxk + 10];
inline int query_pre(int x,int y){
if(x < 0 || y < 0)return 0;
return pre[x][y];
}
int main(){
n = read();
for(int i = 1;i <= n;i++)
val[i].x = read(),val[i].y = read();
for(int i = 1;i <= n;i++)
tmp[val[i].x][val[i].y]++;
pre[0][0] = tmp[0][0];
for(int i = 1;i <= 650;i++)
pre[0][i] = pre[0][i - 1] + tmp[0][i];
for(int i = 1;i <= 650;i++)
pre[i][0] = pre[i - 1][0] + tmp[i][0];
for(int i = 1;i <= 650;i++)
for(int j = 1;j <= 650;j++)
pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + tmp[i][j];
suf[650][650] = tmp[650][650];
for(int i = 649;i >= 0;i--)
suf[650][i] = suf[650][i + 1] + tmp[650][i];
for(int i = 649;i >= 0;i--)
suf[i][650] = suf[i + 1][650] + tmp[i][650];
for(int i = 649;i >= 0;i--)
for(int j = 649;j >= 0;j--)
suf[i][j] = suf[i + 1][j] + suf[i][j + 1] - suf[i + 1][j + 1] + tmp[i][j];
for(int i = 1;i <= n;i++){
int x = val[i].x,y = val[i].y;
int ans2 = query_pre(x - 1,y - 1) + (x == 650) * tmp[0][y] + (y == 650) * tmp[x][0];
printf("%d %d
",1 + suf[x + 1][y + 1],n - ans2);
}
return 0;
}