2021杭电多校第九场 1003——Dota2 Pro Circuit

题目大意

n n n 个人,每个人有一个初始分值,然后再给你 n n n 个数,表示接下来一场比赛获得第 i i i 名的得分。问你这场比赛之后每个人可能获得的最好名次和最坏名次。
分数并列则名次并列,且并列名次跳过,如四个人得分为5 3 3 2,那么排名是1 2 2 4。
n < = 50001 < = T < = 20 n <= 5000 1 <= T <= 20 n<=50001<=T<=20,最多有8组数据 n > 100 n > 100 n>100

解题思路

考虑直接暴力每个人的最好名次和最坏名次复杂度是 O ( n 2 l o g n ) = k e 9 O(n^2logn) = ke9 O(n2logn)=ke9,杭电的评测机是跑不过了
我们先将初始分值 a i a_i ai 和 下一场的得分 b i b_i bi 从小到大排序。
考虑第 i i i 个人的最佳名次
设初始最佳名次为1,得分为 n o w = a i + b n now = a_i + b_n now=ai+bn,显然想获得最佳名次肯定是拿最高的分,我们可以想,第1到 i − 1 i-1 i1个人的得分一定不会超过第 i i i 个人,(因为他们初始分比第 i i i 个人少,得分又比第 i i i 个人少,所以名次一定更差),所以我们只需要考虑 i + 1 i+1 i+1 n n n
设第 j j j 个人的获得的所拿的分为 b k b_k bk,如果 a j + b k > n o w a_j + b_k > now aj+bk>now,那么我们让第 j j j 个人拿 第 b k − 1 b_{k-1} bk1,直到 a j + b k < = n o w a_j + b_k <= now aj+bk<=now,这样才不会影响第 i i i 个人的排名。如果 k = 0 k = 0 k=0,说明从第 j j j 个人开始,他们的得分一定比 i i i 高,我们直接让 i i i 的排名加上 n − j + 1 n-j+1 nj+1即可。
为什么可以这样呢?因为如果第 j j j 个人不拿 b k b_k bk(说明 a j + b k > n o w a_j + b_k > now aj+bk>now),那么第 j + 1 j+1 j+1 个人也一定不会拿 b k b_k bk,因为 a j + 1 + b k > a j + b k > n o w a_{j+1} + b_k > a_j + b_k > now aj+1+bk>aj+bk>now,同样会超过第 i i i 个人的得分。这样我们就可以去掉一个 l o g n logn logn的复杂度。

考虑第 i i i 个人的最坏名次也是同理,不过我们,要注意一下,如果初始化最坏名次为 n n n ,那么如果有多个人初始得分相同,我们一定要从最后一个开始跑,(因为相同分数,排名是记在前面的),而且初始得分相同,那么最坏排
名也一定相同,所以我们跑一个就可以了
最终复杂度 O ( n 2 ) O(n^2) O(n2)

Code

#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 5007;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int n;
struct node{
    ll x, idx;
    bool operator<(node b) const{
        return x < b.x;
    }
}a[MAXN];
ll b[MAXN];
void solve(){
    cin >> n;
    for (int i = 1; i <= n; ++i){
        cin >> a[i].x;
        a[i].idx = i;
    }
    for (int i = 1; i <= n; ++i){
        cin >> b[i];
    }
    sort(a+1,a+1+n);
    sort(b+1, b+1+n);
    vector<PII> ans(n+1);
    a[n+1].x = -1;
    int ls = 1;
    for (int i = 1; i <= n; ++i){
        int l = 1, r = n;
        ll now = a[i].x + b[r--];
        int pm = 1;
        for (int j = i+1; j <= n; ++j){
            while(l <= r && a[j].x + b[r] > now) {
                r--;    
            }
            if(l > r){
                pm += (n - j + 1);
                break;
            }
            r--;
        }
        ans[a[i].idx].fi = pm;

        if(a[i].x == a[i+1].x)
            continue;
        pm = n;
        l = 1, r = n;
        now = a[i].x + b[l++];
        for (int j = i-1; j >= 1; --j){
            while(l <= r && a[j].x + b[l] <= now) {
                l++;
            }
            if(l > r){
                pm -= j;
                break;
            }
            l++;
        }
        for (int j = ls; j <= i; ++j){
            ans[a[j].idx].se = pm;
        }
        ls = i+1;
    }
    for (int i = 1; i <= n; ++i){
        cout << ans[i].fi << " " << ans[i].se << endl;
    }
}

int main()
{
    #ifdef ONLINE_JUDGE
    #else
       freopen("in.txt", "r", stdin);
       freopen("out.txt", "w", stdout);
    #endif

    qc;
    int T;
    cin >> T;
    // T = 1;
    while(T--){

        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值