题目描述
在 Byteland,竞赛编程非常流行。事实上,每位 Byteland 的公民都在两个编程网站------CodeCoder 和 TopForces 上注册。每个网站都有自己专有的评分系统。每位公民在每个网站上都有一个唯一的整数评分,代表他们的技能。评分越高,技能越好。
Byteland 的人天生乐观。公民 A 认为,如果存在一个 Byteland 公民的序列 A = P 0 , P 1 , . . . , P k = B A = P_{0}, P_{1},...,P_{k} = B A=P0,P1,...,Pk=B,对于某个 k ≥ 1 k \ge 1 k≥1,使得对于每个 i ( 0 ≤ i < k ) i (0 \le i < k) i(0≤i<k), P i P_{i} Pi 在一个或两个网站上的评分都高于 P i + 1 P_{i+1} Pi+1,那么他就有机会在编程比赛中击败公民 B。
每位 Byteland 公民都想知道他们在编程比赛中可能击败多少其他公民。
输入格式
输入的第一行包含一个整数 n n n------公民的数量 ( 1 ≤ n ≤ 100000 ) (1 \le n \le 100 000) (1≤n≤100000)。接下来的 n n n 行包含关于评分的信息。第 i i i 行包含两个整数 C C i CC_{i} CCi 和 T F i TF_{i} TFi------第 i i i 位公民在 CodeCoder 和 TopForces 上的评分 ( 1 ≤ C C i , T F i ≤ 10 6 ) (1 \le CC_{i}, TF_{i} \le 10^{6}) (1≤CCi,TFi≤106)。每个网站上的所有评分都是不同的。
输出格式
对于每位公民 i i i,输出一个整数 b i b_{i} bi------他们在编程比赛中可能击败的其他公民数量。每个 b i b_{i} bi 应该单独一行输出,顺序与输入中给出的公民顺序相同。
输入输出样例 #1
输入 #1
4
2 3
3 2
1 1
4 5
输出 #1
2
2
0
3
题意
有 n n n 个人,他们每人都有两个评分 C C i , T F i CC_i,TF_i CCi,TFi。
若公民序列 A = P 0 , P 1 , . . . , P k = B A = P_{0}, P_{1},...,P_{k} = B A=P0,P1,...,Pk=B,满足每一个 P i P_i Pi 至少有一个分数严格大于 P i + 1 P_{i + 1} Pi+1,则称为 A A A 能击败 B B B。
求每个公民可能击败多少其他公民。
思路
我们先推导能击败的等价条件:
-
若 X X X 能击败 Y Y Y, Y Y Y 能击败 Z Z Z,则 X X X 能击败 Z Z Z;
-
记 M ( i ) M(i) M(i) 为 i i i 能击败的所有人中最大的 T F TF TF 值,则:
- i i i 能击败所有 T F ≤ M ( i ) TF \leq M(i) TF≤M(i) 的人;
- i i i 无法击败任何 T F > M ( i ) TF > M(i) TF>M(i) 的人。
由于每个人分数唯一,我们将所有人按 C C CC CC 升序排序:
-
排序后第 i i i 个人的 C C CC CC 是第 i i i 小的,前 i − 1 i-1 i−1 个人的 C C CC CC 都更小,因此这 i − 1 i-1 i−1 个人都会被第 i i i 个人击败;
-
预处理前缀最大 T F TF TF: m a x n i = max ( m a x n i − 1 , T F i ) maxn_i = \max(maxn_{i-1}, TF_i) maxni=max(maxni−1,TFi),这是第 i i i 个人能有的最大 T F TF TF 值。
排序后 C C CC CC 递增,后面的人会影响前面的人,因此我们需要从后往前遍历:
-
用树状数组维护 T F TF TF 坐标上的「最大击败人数」;
-
对第 i i i 个人:查询 T F < m a x n i TF < maxn_i TF<maxni 的最大击败人数,与 i − 1 i-1 i−1 取最大值,即为最终答案;
-
将当前 T F TF TF 位置更新为该答案。
复杂度取决于排序的 O ( n log n ) ( n = 10 5 ) O(n \log n)(n = 10^5) O(nlogn)(n=105) 和树状数组的 O ( n log V ) ( V = 10 6 ) O(n \log V)(V = 10^6) O(nlogV)(V=106),所以总复杂度 O ( n log V ) O(n \log V) O(nlogV),时间复杂度是可行的。
代码
cpp
#include<bits/stdc++.h>
using namespace std;
struct did
{
int cc,tf,id,maxn;//每个公民的两种分数、输入时的编号以及前缀最大
bool operator<(did other)const//重载运算符,按 CC 升序排序
{
return cc<other.cc;
}
}p[1000010];
int n,c[1000010],ans[1000010];
int lowbit(int i)
{
return (-i)&i;
}
void add(int i,int z)
{
for(;i<=1000001;i+=lowbit(i))
c[i]=max(c[i],z);
}
int sum(int i)
{
int ans=0;
for(;i>0;i-=lowbit(i))
ans=max(ans,c[i]);
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>p[i].cc>>p[i].tf;
p[i].id=i;//记录输入时的编号
}
sort(p+1,p+1+n);//升序排序
for(int i=1;i<=n;i++)
p[i].maxn=max(p[i-1].maxn,p[i].tf);//预处理前缀最大 TF
for(int i=n;i>=1;i--)//从后往前
{
ans[p[i].id]=max(i-1,sum(p[i].maxn-1));//求出最终答案
add(p[i].tf,ans[p[i].id]);//更新答案
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<'\n';
return 0;
}