2023 年 CCPC 网络预选赛 L.Partially Free Meal (主席树)
大致题意:
给定数组a和数组b,需要挑选i(1 <= i <= n)个下标,对于选定的这个i个下标x1, x2... xi价值是在a[x1] + a[x2] + ... a[xi] + max(b[x1], b[x2], ..., b[xi]), 对于每个相同的i下标不能重复选,可以不连续。求每个i的最小值。
解题思路
通过观察发现,当i增加的时候maxb是不递减的,所以考虑按照b数组排序求解,解一个位置的答案可以枚举b,然后再主席树里面求当前版本往前最小的一些数字的和。我们枚举出区间[l, r]的 mid = l + r >> 1解的b的位置在x, 那么[l, mid - 1]的答案b的下标一定不大于x, [mid + 1, r]的答案里面b的下标一定不小于x。所以我们按照中点每次向左右递归的话最多只有logn层,所以时间复杂度为n * log(n) * log(n)。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define ll long long
#define fs first
#define se second
const long double eps = 1e-9;
const int N = 2e5 + 10, M = 2e5 + 10;
const int MOD = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n , m, _;
std::vector<int> nums;
struct node{
int l, r, cnt;
ll sum;
}tr[N * 21];
/
struct Historical_Segment_Tree{
int idx;
int root[N];
inline int build(int l, int r){
int q = ++ idx;
if(l == r) return q;
int mid = l + r >> 1;
tr[q].l = build(l, mid);//存的是左儿子的编号并非边界
tr[q].r = build(mid + 1, r);
return q;
}
inline int insert(int p, int l, int r, int x, int sum){//需要用到的上一个版本root[i - 1](返回的值是当前版本的root)
int q = ++ idx;
tr[q] = tr[p];//复制上一个节点的信息
if(l == r){
tr[q].cnt += sum;//新版本的信息加1
tr[q].sum += nums[x];
return q;
}
int mid = l + r >> 1;
if(x <= mid) tr[q].l = insert(tr[p].l, l, mid, x, sum);//在左子树则需要更新信息,否则保留原本信息就可以
else tr[q].r = insert(tr[p].r, mid + 1, r, x, sum);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
tr[q].sum = tr[tr[q].l].sum + tr[tr[q].r].sum;
return q;
}
inline int query(int p, int q, int L, int R, int l, int r){
if(L >= l && R <= r) return tr[q].cnt - tr[p].cnt;
int mid = L + R >> 1;
int cnt = 0;
if(l <= mid) cnt += query(tr[p].l, tr[q].l, L, mid, l, r);
if(r > mid) cnt += query(tr[p].r, tr[q].r, mid + 1, R, l, r);
return cnt;
}
inline ll query(int p, int q, int l, int r, int k){//区间k小
if(l == r) return 1ll * nums[l] * k;
int mid = l + r >> 1;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
if(cnt >= k) return query(tr[p].l, tr[q].l, l, mid, k);
return tr[tr[q].l].sum + query(tr[p].r, tr[q].r, mid + 1, r, k - cnt);
}
}HST;
struct NODE {
int a, b;
bool operator < (const NODE &x) const {
return this->b < x.b;
}
}t[N];
ll ans[N];
auto &root = HST.root;
inline void Solution(int xl, int xr, int yl,int yr) {
if (xl > xr) return ;
int mid = xl + xr >> 1;
int tmp = 0;
ll tans = INF;
for (int i = std::max(mid, yl); i <= yr; i ++) {
ll now = HST.query(root[0], root[i], 0, nums.size() - 1, mid);
now += t[i].b;
if (now < tans) {
tans = now;
tmp = i;
}
}
ans[mid] = tans;
Solution(xl, mid - 1, yl, tmp), Solution(mid + 1, xr, tmp, yr);
}
inline void solve(){
std::cin >> n;
for (int i = 1; i <= n; i ++) {
std::cin >> t[i].a >> t[i].b;
nums.push_back(t[i].a);
}
std::sort(t + 1, t + n + 1);
std::sort(nums.begin(), nums.end());
nums.erase(std::unique(nums.begin(), nums.end()), nums.end());
root[0] = HST.build(0, nums.size() - 1);
for (int i = 1; i <= n; i ++) {
int pos = std::lower_bound(nums.begin(), nums.end(), t[i].a) - nums.begin();
root[i] = HST.insert(root[i - 1], 0, nums.size() - 1, pos, 1);
}
Solution(1, n, 1, n);
for (int i = 1; i <= n; i ++) std::cout << ans[i] << '\n';
}
signed main(void){
_ = 1;
//std::cin >> _;
while(_ --)
solve();
return 0;
}