@codeforces - [email protected] T-Shirts

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量。
同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt。

每个顾客的购买策略是相同的:
他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种 T-shirt 只买1 次。
重复购买,直到所有的 T-shirt 他都买不起或者他都买过了。

求每位顾客最终可以购买的 T-shirt 数量。

Input
第一行一个整数 n (1?≤?n?≤?2·10^5),表示 T-shirt 的种类数。
接下来 n 行,每行两个整数 ci 与 qi (1?≤?ci,?qi?≤?10^9),含义如上。
接下来一行一个正整数 k (1?≤?k?≤?2·10^5) ,表示顾客数。
接下来 k 个正整数 b1,?b2,?...,?bk (1?≤?bj?≤?10^9)。

Output
输出一行 k 个整数,表示第 i 个人买的 T-shirt。

Examples
Input
3
7 5
3 5
4 3
2
13 14
Output
2 3

Input
2
100 500
50 499
4
50 200 150 100
Output
1 2 2 1

@[email protected]

假如我们把人当作询问,T-shirt 当作元素,则至少我是想不出什么办法快速维护求出询问的。

但是反过来——我们按照 T-shirt 的优先级从大到小排序,把每个 T-shirt 当作一次修改,然后对人进行统一地修改,就很有戏了。
假如一个 T-shirt 的价格是 c,则我们的修改步骤就是先找出 >= c 的人,将这些人所拥有的钱减去 c,将这些人所拥有的 T-shirt 数量加上 1。

貌似可以用平衡树来 merge 和 split。但是注意,我们只能 merge 两个值域没有交集的平衡树,而减去 c 过后可能就产生了交集。
我们将 >= c 的人分为两类。第一类是 >= 2*c 的,这一部分减去 c 依然可以直接 merge;第二类是 < 2*c 的,这一类减去 c 过后只能一个个暴力插入。

因为第二类的人一定满足 c <= x < 2*c,每一次 x 至少折半,这样每个人只会被暴力插入 O(log)
于是就 O(nlog^2n) 的总复杂度。因为是 codeforces 所以能过。

@accepted [email protected]

#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 200000;
struct treap{
    struct node{
        node *ch[2];
        int key, tg1;
        int sum, tg2;
        unsigned int pri;
    }pl[MAXN + 5], *ncnt, *NIL;
    #define mp make_pair
    #define fi first
    #define se second
    typedef pair<node*, node*> Droot;
    unsigned int get_rand() {return (rand() << 16) | rand();}
    treap() {
        NIL = ncnt = &pl[0];
        NIL->ch[0] = NIL->ch[1] = NIL;
        NIL->key = NIL->tg1 = NIL->sum = NIL->tg2 = 0;
        NIL->pri = -1;
    }
    node *newnode(int k) {
        node *p = (++ncnt);
        p->ch[0] = p->ch[1] = NIL;
        p->key = k, p->tg1 = p->sum = p->tg2 = 0;
        p->pri = get_rand();
        return p;
    }
    void maintain1(node *x, int k) {if( x != NIL ) x->key += k, x->tg1 += k;}
    void maintain2(node *x, int k) {if( x != NIL ) x->sum += k, x->tg2 += k;}
    void pushdown(node *rt) {
        if( rt->tg1 ) {
            maintain1(rt->ch[0], rt->tg1), maintain1(rt->ch[1], rt->tg1);
            rt->tg1 = 0;
        }
        if( rt->tg2 ) {
            maintain2(rt->ch[0], rt->tg2), maintain2(rt->ch[1], rt->tg2);
            rt->tg2 = 0;
        }
    }
    node *merge(node *x, node *y) {
        if( x == NIL ) return y;
        if( y == NIL ) return x;
        pushdown(x), pushdown(y);
        if( x->pri < y->pri ) {
            x->ch[1] = merge(x->ch[1], y);
            return x;
        }
        else {
            y->ch[0] = merge(x, y->ch[0]);
            return y;
        }
    }
    Droot split(node *rt, int k) {
        if( rt == NIL ) return mp(NIL, NIL);
        pushdown(rt);
        if( rt->key < k ) {
            Droot p = split(rt->ch[1], k);
            rt->ch[1] = p.fi; return mp(rt, p.se);
        }
        else {
            Droot p = split(rt->ch[0], k);
            rt->ch[0] = p.se; return mp(p.fi, rt);
        }
    }// < k, >= k
    node *insert(node *rt, node *x) {
        Droot p = split(rt, x->key);
        return merge(p.fi, merge(x, p.se));
    }
    void push(node *&rt, node *x) {
        if( x == NIL ) return ;
        pushdown(x);
        push(rt, x->ch[0]), push(rt, x->ch[1]);
        x->ch[0] = x->ch[1] = NIL;
        rt = insert(rt, x);
    }
    node *update(node *rt, int k) {
        Droot p = split(rt, k);
        if( p.se == NIL ) return merge(p.fi, p.se);
        p.se->key -= k, p.se->tg1 -= k;
        p.se->sum += 1, p.se->tg2 += 1;
        Droot q = split(p.se, k);
        if( q.fi != NIL ) push(p.fi, q.fi);
        return merge(p.fi, q.se);
    }
    void dfs(node *x) {
        if( x == NIL ) return ;
        pushdown(x);
        dfs(x->ch[0]), dfs(x->ch[1]);
    }
}T;
treap::node *nd[MAXN + 5], *rt;
struct Tshirt{
    int c, q;
    Tshirt(int _c=0, int _q=0):c(_c), q(_q) {}
    friend bool operator < (Tshirt a, Tshirt b) {
        return (a.q == b.q) ? a.c < b.c : a.q > b.q;
    }
}t[MAXN + 5];
int ans[MAXN + 5];
int read() {
    int x = 0; char ch = getchar();
    while( ch > '9' || ch < '0' ) ch = getchar();
    while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    return x;
}
int main() {
    int n = read(), k;
    srand(n ^ 0307);
    for(int i=1;i<=n;i++)
        t[i].c = read(), t[i].q = read();
    sort(t + 1, t + n + 1);
    k = read(), rt = T.NIL;
    for(int i=1;i<=k;i++)
        rt = T.insert(rt, nd[i] = T.newnode(read()));
    for(int i=1;i<=n;i++)
        rt = T.update(rt, t[i].c);
    T.dfs(rt);
    for(int i=1;i<=k;i++)
        printf("%d ", nd[i]->sum);
}

@[email protected]

思路本身还是很巧妙的。
而且也印证了非旋 treap 是一种非常优美的数据结构,因为基本上没有什么实现难度。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11693185.html

时间: 2024-10-29 04:40:52

@codeforces - [email protected] T-Shirts的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m

@codeforces - [email&#160;protected] Koala and Notebook

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n 点 m 边的无向连通图,每条边的编号按照输入顺序依次为 1, 2, ..., m. 现从 1 号点出发,当经过编号为 i 的边时,将 i 写下来.因为写的数之间没有空隙,所以写下来的所有数最终会连成一个数. 对于每一个除 1 以外的点,当它作为终点时,最终连成的数最小是多