【CROATIAN OPEN COMPETITION IN INFORMATICS 3rd round, November 29th, 2014】HONI

题目:比赛已经进行到了第三场,开始有人预测第三场比赛选手们的排名了。怎么预测呢,我们假设存在以下的规律:
如果选手A在前两场比赛的分数都高于B,则第三场比赛选手A的分数不可能低于B。
排名的规则是这样的,总分相同则名次并列。比如说,如果有5个选手的分数为1000,1000,900,900,800,则他们的名次为1,1,3,3,5.
现在我们知道N个选手在前两场比赛的成绩,如果上述规律始终成立,预测每个选手的最高排名和最低排名。
输入:第一行一个整数N,表示有N个选手。(N<=500000)
接下来有N行,每行两个整数S1,S2,在0~650之间。表示前两场比赛选手的分数。
输出:对每个选手,给出两个整数,表示他可能的最高排名和可能的最低排名。

分析:计算i不可能超过的人,对于选手i,如果存在选手j满足S1i<S1j,S2i<S2j,则i不可能超过j或者与j得分相同。所以统计选手j的个数,个数+1即为i的最高排名。
计算不可能超过i的人,如果存在选手k满足S1k<S1k,S2k<S2k,则k不可能超过i或者与i得分相同,但我们还要考虑与最终可能与i得分相同的人,即两人其中一场得分一样,另一场i得了650分,另一人没得分。
用类似dp的方法。
f2[i][j]表示i不可能超过的人,f1反之。

代码:

#include<cstdio>
#define MAXN 500000
#define MAXS 650
int n,s1[MAXN+10],s2[MAXN+10];
int a[MAXS+5][MAXS+5],f1[MAXS+5][MAXS+5],f2[MAXS+5][MAXS+5];
void Read(int &x){
	char c;
	while(c=getchar(),c!=EOF){
		if(c>='0'&&c<='9'){
			x=c-'0';
			while(c=getchar(),c>='0'&&c<='9')
				x=x*10+c-'0';
			ungetc(c,stdin);
			return;
		}
	}
}
void read(){
	int i;
	Read(n);
	for(i=1;i<=n;i++){
		scanf("%d%d",&s1[i],&s2[i]);
		a[s1[i]][s2[i]]++;
	}
}
void solve(){
	int i,j;
	for(i=0;i<=MAXS;i++){
		for(j=0;j<=MAXS;j++){
			if(i>0&&j>0)
				f1[i][j]+=a[i-1][j-1]-f1[i-1][j-1];
			if(i>0)
				f1[i][j]+=f1[i-1][j];
			if(j>0)
				f1[i][j]+=f1[i][j-1];
		}
	}
	for(i=MAXS;i>=0;i--){
		for(j=MAXS;j>=0;j--){
			f2[i][j]+=a[i+1][j+1]-f2[i+1][j+1];
			f2[i][j]+=f2[i][j+1];
			f2[i][j]+=f2[i+1][j];
		}
	}
}
void print(){
	for(int i=1;i<=n;i++)
		printf("%d %d\n",1+f2[s1[i]][s2[i]],n-f1[s1[i]][s2[i]]-a[0][s2[i]]*(s1[i]==650)-a[s1[i]][0]*(s2[i]==650));
}
int main()
{
	read();
	solve();
	print();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值