cf Educational Codeforces Round 141 C. Yet Another Tournament

原题:
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

你可能感兴趣的:(贪心\模拟\STL\暴力,c语言,开发语言)