题目链接:https://ac.nowcoder.com/acm/contest/6488/C
Description
牛妹在银行排队等号时,观察到以下场景。
银行有m个服务窗口,假设当前有n个人等待办理业务,那么这n个人会被顺序分配一个从1到n的号码。
等待办理业务的流程如下:
从第1号到第n号顺序的进行排队。
假设当前第1号到第i-1号都正在办理或已经办理完业务,且某个窗口A没有客人正在办理业务,那么第i号会马上到窗口A办理他的业务。
如果有多个这样的窗口,第i号会随意选择一个窗口。
在0时刻,牛妹观察到m个窗口都没有客人正在办理业务,而n个人正在等待办理业务。
为了简化问题,我们假设第i号不管在哪个窗口办理业务,办理业务的时间都为ai。
牛妹想知道有多少对(i,j),满足1≤i<j≤n,且第i号办理业务完成的时间严格大于第j号办理业务完成的时间。
Sample Input
5,2,[1,3,2,5,4]
Sample Output
1
Interpretation
第1号 开始办理时间 0 办理完成时间 1
第2号 开始办理时间 0 办理完成时间 3
第3号 开始办理时间 1 办理完成时间 3 (在1号办理完同时开始办理)
第4号 开始办理时间 3 办理完成时间 8 (在2和3号办理完同时开始办理)
第5号 开始办理时间 3 办理完成时间 7 (在2和3号办理完同时开始办理)
唯一一组满足题意的(i,j)对为(4,5)
Hint
对于30%数据,1≤n,m≤1000。
对于100%数据,1≤n,m≤100000,1≤ai≤1e9
Solution
听了雨巨的讲解才会做!呜呜呜
显然我们首先要求出每个人办理业务完成的时间,那么我们用一个小根堆来维护即可。
接下来我们只需要求出逆序对的个数即可。
求逆序对的个数一般可以用归并排序、树状数组。很幸运我都不会,哭死
那么就让我们来写一下归并排序求逆序对的个数吧!(Ctrl CV大法)
Code
class Solution {
public:
/**
* 求解合法的(i,j)对的数量
* @param n int整型 n个人
* @param m int整型 m个窗口
* @param a int整型vector 长度为n的vector,顺序表示1-n号客人的办理业务所需时间
* @return long长整型
*/
long long ans;
long long b[100010], t[100010];
void merge(int L, int mid, int R) {
long long p = L, q = mid + 1;
for (int i = L; i <= R; i++) {
if (p <= mid && (t[p] <= t[q] || q > R)) {
b[i] = t[p];
p++;
}
else {
b[i] = t[q];
q++;
ans+=mid - p + 1;//因为已排好序,当前位置大,后续的必定也大
}
}
for (int i = L; i <= R; i++) t[i] = b[i];
}
void mergesort(int L, int R) {
if (L >= R) return;
int mid = (L + R) / 2;
mergesort(L, mid);
mergesort(mid + 1, R);
merge(L, mid, R);//合并
}
long long getNumValidPairs(int n, int m, vector<int>& a) {
// write code here
priority_queue<long long, vector<long long>, greater<long long> > p;//小根堆
for (int i = 0; i < min(n, m); i++) {
for (int i = 0; i < min(n, m); i++) {
t[i] = a[i];
p.push(t[i]);
}
for (int i = m; i < n; i++) {
long long temp = p.top();
p.pop();
t[i] = a[i] + temp;
p.push(t[i]);
}
ans = 0;
mergesort(0, n - 1);//归并排序
return ans;
}
};