zoukankan      html  css  js  c++  java
  • CF702F T-Shirts(平衡树)

    CF702F T-Shirts(平衡树)

    题目大意

    (n) 种T恤,每种有价格 (c_i) 和品质 (q_i)

    (m) 个人要买 (T) 恤,第 (i) 个人有 (v_i) 元,每人每次都会买一件能买得起的 (q_i) 最大的T恤。一个人只能买一种T恤一件,所有人之间都是独立的。

    问最后每个人买了多少件 (T) 恤?如果有多个 (q_i) 最大的T恤,会从价格低的开始买。

    数据范围

    $ 1le nle 2·10^{5} $

    $ 1le c_{i},q_{i} le 10^{9} $

    (1le m le 2·10^{5})

    解题思路

    思维题

    考虑两种暴力

    第一种是一个一个人扫一遍,没什么好办法优化

    第二种是所有人一起,碰到一个新T恤就把能买的人都买

    你思考是否可以用平衡树解决这个问题,在当前这颗树中,将小于价格的保留,大于价格的都减去价格

    不妨设价格为 c

    但是减去 c 之后相对顺序将被打乱,使用 fhq 的话也要求右边的最小值一定要大于左边的最大值

    如果暴力将右边小于左边最大值的数暴力插过去可行吗

    答案是肯定的

    考虑什么样的数会被暴力插,大于 (2c) 的数减 c 仍然大于左边,而在 (c, 2c) 之间的数才会暴力,不难发现这样的数减 c 之后数值至少缩小一半,也就是说所有的数最多会被暴力插 (log N)

    总复杂度 (Theta(NlogN))

    代码(竟然写完没调一遍过了,且常数较小

    #define ls son[x][0]
    #define rs son[x][1]
    const int N = 200500;
    int tag[N], val[N], rnd[N], son[N][2], add[N], ans[N], cnt;
    int build(int x) {
    	val[++cnt] = x, rnd[cnt] = rand();
    	return cnt; 
    }
    
    void spread(int x) {
    	if (tag[x]) {
    		if (ls) tag[ls] += tag[x], ans[ls] += tag[x];
    		if (rs) tag[rs] += tag[x], ans[rs] += tag[x];
    		tag[x] = 0;
    	}
    	if (add[x]) {
    		if (ls) add[ls] += add[x], val[ls] += add[x];
    		if (rs) add[rs] += add[x], val[rs] += add[x];
    		add[x] = 0;
    	}
    }
    
    void split(int nw, int k, int &x, int &y) {
    	if (!nw) return x = y = 0, void();
    	spread(nw);
    	if (val[nw] <= k) x = nw, split(rs, k, rs, y);
    	else y = nw, split(son[y][0], k, x, son[y][0]);
    }
    
    int getmx(int rt) {
    	while (son[rt][1]) 
    		spread(rt), rt = son[rt][1];
    	return val[rt];
    }
    
    int merge(int x, int y) {
    	if (!x || !y) return x | y;
    	if (rnd[x] < rnd[y]) return spread(x), rs = merge(rs, y), x;
    	return spread(y), son[y][0] = merge(x, son[y][0]), y;
    }
    
    void insert(int &rt, int k) {
    	int x, y;
    	split(rt, val[k], x, y);
    	rt = merge(merge(x, k), y);
    }
    
    struct node {
    	int c, q;
    	bool operator < (const node &i) const {
    		if (q != i.q) return q > i.q;
    		return c < i.c;
    	}
    }t[N];
    
    int f[N], b[N], m, n;
    bool cmp(int a, int y) {
    	return b[a] < b[y];
    }
    
    void rebuild(int nw, int &rt) {
    	if (!nw) return;
    	spread(nw);
    	rebuild(son[nw][0], rt);
    	rebuild(son[nw][1], rt);
    	son[nw][0] = son[nw][1] = 0;
    	insert(rt, nw);
    }
    
    void spreadit(int x) {
    	if (!x) return;
    	spread(x);
    	spreadit(ls), spreadit(rs);
    }
    
    int rt;
    int main() {
    	read(m);
    	for (int i = 1;i <= m; i++) 
    		read(t[i].c), read(t[i].q);
    	sort(t + 1, t + m + 1);
    	read(n);
    	for (int i = 1;i <= n; i++) 
    		read(b[f[i] = i]), build(b[i]);
    	sort(f + 1, f + n + 1, cmp);
    	for (int i = 1;i <= n; i++) 
    		rt = merge(rt, f[i]);
    	for (int i = 1;i <= m; i++) {
    		int x, y, z;
    		split(rt, t[i].c - 1, x, y);
    		if (!y) { rt = x; continue; }
    		val[y] -= t[i].c, ans[y]++;
    		add[y] -= t[i].c, tag[y]++;
    		int mx = getmx(x);
    		split(y, mx - 1, z, y);
    		rebuild(z, x), rt = merge(x, y);
    	}
    	spreadit(rt);
    	for (int i = 1;i <= n; i++) write(ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    octotree神器 For Github and GitLab 火狐插件
    实用篇如何使用github(本地、远程)满足基本需求
    PPA(Personal Package Archives)简介、兴起、使用
    Sourse Insight使用过程中的常使用功能简介
    Sourse Insight使用教程及常见的问题解决办法
    github 遇到Permanently added the RSA host key for IP address '192.30.252.128' to the list of known hosts问题解决
    二叉查找树的C语言实现(一)
    初识内核链表
    container_of 和 offsetof 宏详解
    用双向链表实现一个栈
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13295143.html
Copyright © 2011-2022 走看看