原题:
C. Yet Another Tournament
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You are participating in Yet Another Tournament. There are n+1 participants: you and n other opponents, numbered from 1 to n.
Each two participants will play against each other exactly once. If the opponent i plays against the opponent j, he wins if and only if i>j.
When the opponent i plays against you, everything becomes a little bit complicated. In order to get a win against opponent i, you need to prepare for the match for at least a i a_i ai minutes — otherwise, you lose to that opponent.
You have m minutes in total to prepare for matches, but you can prepare for only one match at one moment. In other words, if you want to win against opponents p 1 , p 2 , … , p k p_1,p_2,…,p_k p1,p2,…,pk, you need to spend a p 1 + a p 2 + ⋯ + a p k a_{p_1}+a_{p_2}+⋯+a_{p_k} ap1+ap2+⋯+apk minutes for preparation — and if this number is greater than m
, you cannot achieve a win against all of these opponents at the same time.
The final place of each contestant is equal to the number of contestants with strictly more wins +
1 . For example, if 3 contestants have 5 wins each, 1 contestant has 3 wins and 2 contestants have 1 win each, then the first 3 participants will get the 1-st place, the fourth one gets the 4-th place and two last ones get the 5-th place.
Calculate the minimum possible place (lower is better) you can achieve if you can’t prepare for the matches more than m minutes in total.
Input
The first line contains a single integer t (1≤t≤10^4) — the number of test cases.
The first line of each test case contains two integers n and m (1≤n≤5⋅10^5; 0≤m≤∑i=1nai) — the number of your opponents and the total time you have for preparation.
The second line of each test case contains n integers a1,a2,…,an (0≤ai≤1000), where ai is the time you need to prepare in order to win against the i-th opponent.
It’s guaranteed that the total sum of n over all test cases doesn’t exceed 5⋅10^5.
Output
For each test case, print the minimum possible place you can take if you can prepare for the matches no more than m minutes in total.
Example
5
4 401
100 100 200 1
3 2
1 2 3
5 0
1 1 1 1 1
4 0
0 1 1 1
4 4
1 2 2 1
outputCopy
1
2
6
4
1
Note
In the first test case, you can prepare to all opponents, so you’ll win 4 games and get the 1-st place, since all your opponents win no more than 3 games.
In the second test case, you can prepare against the second opponent and win. As a result, you’ll have 1 win, opponent 1 — 1 win, opponent 2 — 1 win, opponent 3 — 3 wins. So, opponent 3
will take the 1-st place, and all other participants, including you, get the 2-nd place.
In the third test case, you have no time to prepare at all, so you’ll lose all games. Since each opponent has at least 1 win, you’ll take the last place (place 6).
In the fourth test case, you have no time to prepare, but you can still win against the first opponent. As a result, opponent 1 has no wins, you have 1 win and all others have at least 2 wins. So your place is 4.
中文:
有一个比赛,算上你一共有n+1个人参加。这个n个人的水平按照下标递增的顺序从小到大的给出,即下标i < j,那么nj可以打败ni。每个人会再给出一个对应的数ai,你有一个m点的能力,如果想要打败第i个人,那么需要消耗ai。最后问你,如果想要排名尽量高,能排到多少。
注意排名的计算方法。
比如样例4,四个人对应的ai是1 2 2 1,这四个人在一起比赛能获得的胜利的次数分别是0,1,2,3,你的能力m为4,可以选择打败n1, n4和n3这三个人,那么你获胜的次数是3,因此你可以与n4并列排第一。注意,n3此时的排名是第三,因为你与n4并列第一,占了两个名额!
代码:
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 5e5 + 5;
int n, m, t;
struct node {
int val;
int idx;
};
node ns[maxn];
int pre_sum[maxn];
int mmap[maxn];
int cmp(const node& lhs, const node& rhs) {
if (lhs.val != rhs.val) {
return lhs.val < rhs.val;
} else {
return lhs.idx > rhs.idx;
}
}
int main() {
ios::sync_with_stdio(false);
cin >> t;
while(t--) {
cin >> n >> m;
int a;
flag = 0;
for (int i = 1; i <= n; i++) {
cin >> a;
ns[i].val = a;
ns[i].idx = i;
}
sort(ns + 1, ns + n + 1, cmp);
pre_sum[0] = 0;
for (int i = 1; i <= n; i++) {
pre_sum[i] = pre_sum[i - 1] + ns[i].val;
mmap[ns[i].idx] = i;
}
int ans = n + 1;
for (int i = 1; i <= n; i++) {
if (pre_sum[i] <= m) {
ans = min(ans, n - i + 1);
}
if (ns[i].idx == n) {
if (pre_sum[n] <= m) {
ans = min(ans, 1);
}
continue;
}
int more = ns[i].idx + 1;
int more_pos = mmap[more];
int sum = 0;
if (more_pos > ns[i].idx) {
sum = ns[more_pos].val + pre_sum[ns[i].idx - 1];
} else {
sum = pre_sum[ns[i].idx];
}
//cout << "i = " << i << " sum = " << sum << " more = " << more << " more_pos = " << more_pos << endl;
if (sum <= m) {
ans = min(ans, n - ns[i].idx);
}
}
cout << ans << endl;
}
return 0;
}
解答:
题目别读错了,尤其是计算排名的方式!
如果不算你参加比赛,那么题目中给出的顺序就是每个人排名的顺序。这里考虑,如果我的能力值为m,可以选择k个选手打败,那么这k个选手的a值加一起一定是小于等于m的,如何选择这k个选手是关键。
假设有7个人参加比赛,按照下标的顺序给出如下
1 2 3 4 5 6 7
这里假设这7个人每个人都能打败我,那么这7个下标就表示每个人可以打败对手的个数,比如第7个人可以打败前6个人再加上我,一共可以打败7个人。假设,此时我的能力能够打败3个人(这里假设前3个人的数值加一起小于等于m),那么我至少可以排在第三位,即把1,2,3打败,此时每个选手可以打败对手的个数变为如下:
0 1 2 '3' 4 5 6 7
上面带着单引号的表示我参加比赛并打败了三个人,排在第三。
由此可见,如果能打败k个人,我至少可以排在第k位。
这里考虑是否有排名更高的可能,这里依然假设我能打败3个人,如果我能打败第3 + 1个选手,即把4变成3,那么我就可以与第4位选手并列排在第3位,剩下可以打败两个人随便选择两个数值最小的即可。
例如,我能够打败第1,第2和第4个;或者我可以打败第6个,第7个和第4个,只要能满足打败第4个人,即可以将第4个人的排名拉至第三位。
// 打败第1,2,4
0 1 3 '3' 4 5 6 7
//打败第6,7,4
1 2 3 '3' 4 5 5 6
考虑到如上的特点,那么可以做如下安排,首先将数据记录每个人对应的下标idx,和val表示数值,设置结构体ns记录。并对每个人按照val从小到大排序。
枚举每个人的下标idx,用来当作每次我需要打败的人的个数,此外idx + 1也表示,如果我要打败idx个对手,我要把下标为idx + 1的对手打败,记为more,即将idx + 1的对手的获胜场数拉至为idx。
记录pre_sum[i]表示,表示排序后,n个人的val的前n项和。如果我要打败idx个对手,那么我要计算打败位置为more的对手的a值加上任意idx - 1个a最小的选手的a值的和。
这里有两种情况,一种情况的是排序后,more的位置(记录为more_pos,每个人原始的下标再按照val排序后,记录再mmap中)正好在排序后前idx人当中,此时直接将sum设置为pre_sum[idx];另一种情况是more的位置在idx后面,需要将sum设置为pre_sum[idx - 1] + ns[more_pos].val。
最后判断sum是否满足小于等于m即可。
也有另外一种情况,即more_pos对应的val的值大于m,那就退一步,只计算最小的前idx值是否满足sum,此时的排名为idx + 1。
0 1 1 2 2 4 5 --> val,按照val排序
3 7 2 4 1 5 6 --> idx
0 1 2 4 6 10 15 --> pre_sum
1 2 3 4 5 6 7 -->i 排序后的下标i
比如计算idx为4,more = 4 + 1 = 5,此时mmap[5] = 6,大于4,此时计算
sum = ns[6].val + pre_sum[4 -1] = 5 + 2 = 7
如果计算idx为2,more = 2 + 1 =3,此时mmap[3] = 1,小于2,此时计算
sum = pre_sum[2] = 2