P4812 [COCI2014-2015#3] COCI の Solution
muyang_233 · · 题解
原题传送门
根据题意,我们知道,对于一个选手
所以对于每个选手
但是,本题中分数相同的选手排名相同。
所以,在统计完上面的情况后,我们要注意:对于两个选手,若分数分别为
这是容易证明的:考虑极端情况,
而剩下的情况中,既不存在有一人两轮分数都较高的两个选手,也不存在上面的特殊情况,可以证明,他们的排名考虑上面的统计方法即可。(仍然使用极端情况证明。)
现在我们来考虑怎么统计。
我们不妨对第一轮分数排序,然后对于第二轮分数,使用树状数组维护即可。
具体地,对于一个选手
A(a,b) ,考虑所有第一轮分数严格大于A(a,b) 的选手B ,用他们的第二轮分数更新树状数组即可。小于同理。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=651;
struct NODE{
int x,y,id;
}in[500005];
int n;
int c[1005];
int ans1[500005];
int ans2[500005];
int cnt[maxn+5][maxn+5];
inline void input(int &x){
x=0;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
int p=1;if (ch=='-') p=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();x*=p;
}
void write(int x){
if (x<10){
putchar((char)(x+'0'));return;
}
write(x/10);
putchar((char)(x%10+'0'));
}
inline void output(int x,char c){
if (x<0) {putchar('-');x*=-1;}
write(x);putchar(c);
}
bool cmp(NODE _1,NODE _2){//排序
return _1.x^_2.x?_1.x>_2.x:_1.y>_2.y;
}
int lowbit(int x){
return x&(-x);
}
inline void update(int p){
while(p<=maxn){
++c[p];
p+=lowbit(p);
}
}
inline int query(int p){
int res=0;
while(p>0){
res+=c[p];
p-=lowbit(p);
}
return res;
}
int main(){
freopen("compete.in","r",stdin);
freopen("compete.out","w",stdout);
input(n);
for (int i=1;i<=n;i++){
input(in[i].x);input(in[i].y);
++in[i].x;++in[i].y;//为了树状数组写的方便,这里加个1
in[i].id=i;++cnt[in[i].x][in[i].y];
}
sort(in+1,in+n+1,cmp);//排序
int p=0;//记录上一个第一轮分数不同的位置
for (int i=1;i<=n;i++){
int now=query(in[i].y);//第一轮分数大于,第二轮分数小于等于i的选手个数
int now1=p-now;//用第一轮分数大于i的选手总数减去now,就是两轮分数都大于i的选手个数
ans1[in[i].id]=now1+1;//now1个选手比i分数高,i至少是(now1+1)名
if (in[i].x!=in[i+1].x){//第一轮分数出现不同,更新树状数组
while(p<i){
update(in[++p].y);//更新
}
}
}
memset(c,0,sizeof(c));
int q=n+1;//记录上一个第一轮分数不同的位置
for (int i=n;i>0;i--){
int now2=query(in[i].y-1);//两轮分数都小于i的选手个数
ans2[in[i].id]=n-now2;//now2个选手比i分数低,i至多是(n-now2)名
if (in[i].x==maxn) ans2[in[i].id]-=cnt[1][in[i].y];//特殊情况:650的可以被0的追平
if (in[i].y==maxn) ans2[in[i].id]-=cnt[in[i].x][1];//特殊情况:650的可以被0的追平
if (in[i].x!=in[i-1].x){//第一轮分数出现不同,更新树状数组
while(q>i){
update(in[--q].y);//更新
}
}
}
for (int i=1;i<=n;i++){//输出
output(ans1[i],' ');output(ans2[i],'\n');
}
return 0;
}```