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;
}
posted @ 2023-10-27 09:05  春始于雪之下  阅读(475)  评论(0编辑  收藏  举报