【题解】绍兴一中-7.20-T3

本文介绍了一种使用树状数组解决特定套娃问题的方法。该问题要求找到一系列套娃的最大数量,使得每个套娃都可以放入另一个更大的套娃中,并且满足一定的高度和宽度条件。文章详细解释了算法思路,包括按宽度排序插入、查找匹配的“外套”套娃以及通过树状数组维护状态等关键步骤。

传送门
怎么说呢,这道题目其实不是很难,按照luogu的评级,有紫题的难度适中的那种
思维上其实一般,难的在细节处理,但是我考试时没有想出来正解,只是因为没有花时间在这上面,T3嘛,默认做不出来的,更何况T1是树状数组,谁会想到T3还是树状数组?

70%

按照R从大到小插入,每次看看找H比他大的中最小的那个H做他的“外套”,模仿最长不下降子序列求法
时间复杂度 O ( Q n l o g n ) O(Qnlogn) O(Qnlogn)

100%

一堆询问的话,因为我们按照R从大到小插入,那么 R > = A R>=A R>=A这个限制就不用管了
按照上面的贪心来做,先找到每个套娃的外套,套娃 i i i的高度记为 h i h_i hi,外套高度记为 H i H_i Hi
这个外套是固定不变的
对于每个询问,因为 R &gt; = A R&gt;=A R>=A已经处理好,考虑如何算得答案,只要用当前 H &lt; = B H&lt;=B H<=B的套娃数减去 H 外 套 &lt; = B H_{外套}&lt;=B H<=B的外套数就是 H 没 有 外 套 的 套 娃 &lt; = B H_{没有外套的套娃}&lt;=B H<=B的数量
用树状数组维护就行了
去掉 R &gt; = A R&gt;=A R>=A这个限制只要将询问离线,按照 R R R从大到小排序像套娃一样就行了

比较重要的细节是需要将 H H H给离散掉,其他小细节挺多的
过了这道题以后去看看std是怎么写的,结果看不懂,感觉我自己的代码写的比std清晰一点

Code:

#include <bits/stdc++.h>
#define maxn 2000010
using namespace std;
struct node{
    int r, h, id, H;
}a[maxn], q[maxn], h[maxn];
int ans[maxn], n, Q, tot, d[maxn], p;
int tree1[maxn], tree2[maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c=  getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

bool cmp(node x, node y){ return x.r > y.r; }
bool cmp1(node x, node y){ return x.h < y.h; }

int find(int x){
    int l = 1, r = tot, sum = 0;
    while (l <= r){
        int mid = (l + r) >> 1;
        if (d[mid] >= x) sum = mid, r = mid - 1; else l = mid + 1;
    }
    return sum;
}

void upd1(int x){ if (!x) return; for (; x <= p; x += x & -x) ++tree1[x]; }
int query1(int x){ int sum = 0; for (; x; x -= x & -x) sum += tree1[x]; return sum; }
void upd2(int x){ if (!x) return; for (; x <= p; x += x & -x) ++tree2[x]; }
int query2(int x){ int sum = 0; for (; x; x -= x & -x) sum += tree2[x]; return sum; }

int main(){
    n = read(), Q = read();
    for (int i = 1; i <= n; ++i) a[i].r = read(), h[i].h = read(), h[i].id = i;
    for (int i = n + 1; i <= n + Q; ++i) q[i - n].r = read(), h[i].h = read(), h[i].id = i;
    for (int i = 1; i <= Q; ++i) q[i].id = i;
    sort(h + 1, h + 1 + n + Q, cmp1);
    h[0].h = h[1].h + 1;
    for (int i = 1; i <= n + Q; ++i) 
        (h[i].id <= n ? a[h[i].id].h : q[h[i].id - n].h) = h[i].h == h[i - 1].h ? p : ++p;
    sort(a + 1, a + 1 + n, cmp);
    sort(q + 1, q + 1 + n, cmp);
    for (int i = 1; i <= n; ++i){
        int tmp = find(a[i].h);
        if (tmp) a[i].H = d[tmp], d[tmp] = a[i].h; else
        d[++tot] = a[i].h;
    }
    int j = 1;
    for (int i = 1; i <= Q; ++i){
        while (a[j].r >= q[i].r && j <= n) upd1(a[j].h), upd2(a[j++].H);
        ans[q[i].id] = query1(q[i].h) - query2(q[i].h);
    }
    for (int i = 1; i <= Q; ++i) printf("%d\n", ans[i]);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值