题意:假设有一个n元素的数组(数组的元素索引从1开始),针对这个数组有q个查询请求,每个请求由一对整数li,ri组成,表示数组上一个合法索引区间(1≤li≤ri≤n)。我们可以根据查询的索引来计算出范围内的所有数组元素的一个总和,计算时也需要包含li和ri位置的元素。 通过重新排序数组元素,可以使得这些查询的总和最大。求问我们可以计算的最大总和值是多少。
输入要求:使用分号(";")分隔数据。 第1组为一个整数n(1≤n≤200000),表示数组中的元素数量。 第2组为一个整数q(1≤q≤200000),表示查询数量。 第3组为一个数组,包含n个整数ai(0≤ai≤200000)数组元素 ,元素之间使用空格分隔。 第4~(3+q)组,为q组查询,每一组包含两个空格分隔的整数li和ri(1≤li≤ri≤n)。
举例:输入:8;2;2 0 1 0 3 4 5 6;5 7;3 5。得到数列:2 0 1 0 3 4 5 6,针对这个数列,我们有两组查询:5 7与3 5。将数组重排成0 0 2 3 6 4 5 1,然后取第5~7与3~5位置的全部元素和,可得到最大的总和为(6+4+5) + (2+3+6) = 26。
思路:1. 首先应将各部分提取出来。将分层处符号换为可被stringstream识别切割的“ ”。2. 将数列递增排序,用数组保存数列元素的出现次数。针对给定的区间,对次数进行调整。从在给定区间内索引进行+1处理,出右区间后的第一个索引-1处理(用于后面统计次数时保持原样:cnt[i] += cnt[i - 1])。然后进行索引次数出现统计。执行完后,递增排序。3. 将数列与索引数组依次相乘,即为最优解(保证元素大的使用频率高)。
代码:
/**
@ MIOJ_重排数组求最大和
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+1;
typedef long long LL;
int cnt[maxn]; //元素使用率
struct node {
int left,right;
};
int main(){
string s;
while(getline(cin, s)) {
for(int i = 0; i < s.length(); i++) { //去";"分号符,将字符串按题意读取
if(s[i] == ';') s[i] = ' ';
}
memset(cnt, 0, sizeof(cnt));
node p[maxn];
stringstream ss; //依据空格读取字符
ss.clear();
ss << s; //读入字符串
//cout << s << "\n";
int n, q;
ss >> n >> q;
int num[n + 1];
for(int i = 1; i <= n; i++) ss >> num[i];
sort(num + 1, num + n + 1);
for(int i = 0; i < q; i++){
ss >> p[i].left >> p[i].right;
cnt[p[i].left]++;
cnt[p[i].right + 1]--;
}
for(int i = 2; i <= n; i++) cnt[i] += cnt[i - 1];
//for(int i = 1;i<=n;i++) cout<<cnt[i]<<" ";
sort(cnt + 1, cnt + n + 1);
LL ans = 0;
for(int i = n; i > 0; i--){ //最优化排序后,元素" * "其使用次数即为选择的元素最大和
ans += num[i] * cnt[i];
}
cout << ans << endl;
}
return 0;
}
结果: