题目链接: G. Trader Problem
这道题,首先我们可以观察到,能合并的区间一定是连续的,并且k越大,能够合并的区间越多,因此我们需要将提问离线并排序,按照k的大小将区间逐个合并
这道题巧妙的应用了并查集,因为最后的答案一定是选择n个数字,而我们选择的数字个数,是每个区间中从右往左sz[x]的大小,从右往左是因为要选最大的数。因此还要求个前缀和
#include <bits/stdc++.h>
#define ll long long
using namespace std;
typedef pair<ll, int> PII;
const int N = 4e5+10;
PII a[N], q[N];
ll ans[N], sum, pre[N]; //前缀和
map<ll, vector<int>> mp;
int fa[N], sz[N];
int find(const int &x){
if(x!=fa[x]) return fa[x] = find(fa[x]);
return x;
}
int main() {
int n, m, query; scanf("%d%d%d", &n, &m, &query);
for(int i = 1; i <= n+m; i++) scanf("%lld", &a[i].first), a[i].second = i;
for(int i = 1; i <= query; i++) scanf("%lld", &q[i].first), q[i].second = i;
sort(a+1, a+n+m+1); //将数组排序
sort(q+1, q+query+1); //离线, 区间按照q的大小依次合并
for(int i = 1; i <= n+m+1; i++) {
pre[i] = pre[i-1] + a[i].first;
if(a[i].second <=n) sum += a[i].first, sz[i] = 1; //注意只有能够交易的sz置1
fa[i] = i;
ll tmp = a[i].first - a[i-1].first;
mp[tmp].push_back(i);
}
auto it = mp.begin();
for(int i = 1; i <= query; i++){
while(it != mp.end() && it->first <= q[i].first){ //当这个区间的差值<=k, 就说明这个区间内的点都可以合并
for(auto v:it->second) {
int x = v, y = v-1; //合并x, y
x = find(x), y = find(y); //root为合并的区间的末端点, sz代表这个区间中有多少个可以被交易的(即原数组)商品
//pre[x] - pre[x-sz[x]], x为区间右端点, 由于排好序了, 就选择最右边的sz[x]个数即可
sum -= pre[x] - pre[x-sz[x]] + pre[y] - pre[y-sz[y]]; //答案原来的区间的贡献
fa[y] = fa[x], sz[x] += sz[y]; //一定是左端点合并到右端点
sum += pre[x] - pre[x-sz[x]];
}
it++;
}
ans[q[i].second] = sum;
}
for(int i = 1; i<=query; i++){
printf("%lld\n", ans[i]);
}
return 0;
}
然后还要注意并查集合并,是左边的区间 合并 到右边的区间
还有空间要开4e5!!!起初空间开了2e5就RE了