D. Kevin and Competition Memories
一、题意概述
有n个选手和m个问题,给出每个选手的rating---a(n+1),和题目对应的rating---b(m+1),根据rating大小判断选手能否做出这一题。
现在将所有题目分成 [ ] 组,求每组Kevin的排名,求其和,算出和的最小值;
输出m个最小值,k分别等于1,2,···,m 时。
二,思路讲解
要算排名,那么rating低于Kevin的选手对结果就没有影响,所以可以构造一个动态数组a1,使得a1中存的是所有rating大于Kevin的选手(不包括Kevin);
要算排名,比Kevin排名高的选手中,这些选手的排名对Kevin的排名没有影响,那么可以算出每道题的解题人数,而在此之前,可以改变一下b数组,使得答案更容易得出;
当一个题目的rating 小于等于 Kevin 的rating 时,a1中所有选手都可以解决,此时所有人并列第一,所以可以把这道题的rating设置为LONG_MAX,这样所有人都做不出来,结果是一样的,都是第一名,以此,构造出动态数组b1;
此时,就可以构造动态数组c记录每道题的解题人数,Kevin一道题都解不出来,所有Kevin的排名为这组题目中 解题数目的最大值 + 1 ;
然后对c进行排序,每场比赛的最大排名就是 c[ i * k ]+1;i = [ ];
算法:
构造a1,b1时可用枚举比较的方法;
构造c的时候最容易想到两层循环,但是 n,m <= 3e5 ,和不超过3e5,这样时间复杂度最大为1e10,会T,所有要优化查找,此时可以使用排序+双指针,因为如果一道题可以被一个选手做出,那么rating比该名选手大的人也可以做出,存在单调性,所以可以将a1,b1排序,然后双指针,这样时间就被优化了。
三、AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
void solve(){
ll n,m;
cin >> n >> m;
vector<ll> a(n+1) , b(m+1);
for(ll i = 1 ; i <= n ; ++i) cin >> a[i];
for(ll i = 1 ; i <= m ; ++i) cin >> b[i];
vector<ll> a1;
a1.push_back(-1);
for(ll i = 2 ; i <= n ; ++i){
if(a[i] > a[1]) a1.push_back(a[i]);
}
ll tn = a1.size() - 1;
sort(a1.begin() + 1 , a1.end());
sort(b.begin() + 1 , b.end());
vector<ll> b1;
b1.push_back(-1);
for(ll i = 1 ; i <= m ; ++i){
if(b[i] > a[1]) b1.push_back(b[i]);
else b1.push_back(LONG_MAX);
}
sort(b1.begin() + 1 , b1.end());
vector<ll> c(m+1,0);
ll l = 1;
for(ll i = 1 ; i <= m ; ++i){
ll f = 0;
while(l <= tn){
if(a1[l] >= b1[i]){
f = 1;
break;
}
else l++;
}
if(f == 1) c[i] = tn - l + 1;
else c[i] = 0;
}
sort(c.begin() + 1 , c.end());
for(ll k = 1 ; k <= m ; ++k){
ll ans = 0;
for(ll i = 1 ; i <= m / k ; ++i){
ans += c[i*k] + 1;
}
cout << ans << " ";
}
cout << endl;
}
signed main(){
ll t;
cin >> t;
while(t--){
solve();
}
return 0;
}