Power OJ 2840 伯陵防线【可持久化线段树】

传送门


废话一波:这道题是几个月前比赛的题,当时就我一个人开了这道题,我当时写的可持久化线段树,但是思维出错了,一直WA。
这道题还可以用树状数组或者线段树来写,但是我想不到。。。。
出题人博客:https://blog.csdn.net/swust5120166213/article/details/86703350

解题思路:一对华农兄弟的条件就是两个人均在对方的防御范围内。我们可以认为i为华农兄弟的右兄弟,然后我们去寻找他的左兄弟(j<i)。如果必须是一对华农兄弟,则必须满足条件:(i-j+1<=a_{i})\&\&(a_{j}>=i-j+1)
我们列举出一种比较极端的情况就比较好理解了。

4
4 3 2 4

由上面的例子可以知道,4(右)的防御范围极限就在4(左)的位置。越靠近4(右),华农左兄弟的防御值就可以减少一。
那么可以发现 3 比 4(左) 小1,但是下标却大 1 。4(左)+1(下标)=3+2(下标)=2+3(下边)>=4(右)(下标)+1。
由特殊情况我们可以发现其他情况都是如此。
这样可以知道,我们只需要找区间内大于等于 x 的个数,直接可持久化线段树套模板即可。

坑点:答案记得用 long long,不能离散化,防御范围可能会越界

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int num[maxn], n, root[maxn], times;
long long ans = 0;
struct node {
    int ls, rs, cnt;
#define left(a, b) p[a].ls, p[b].ls, l, mid
#define right(a, b) p[a].rs, p[b].rs, mid + 1, r
} p[maxn * 40];
void insert(int &now, int old, int l, int r, int x) {
    now = ++times, p[now] = p[old], p[now].cnt++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (x <= mid) insert(left(now, old), x);
    else insert(right(now, old), x);
}
int query(int sta, int end, int l, int r, int x) {
    if (l >= x) return p[end].cnt - p[sta].cnt;
    int mid = (l + r) >> 1;
    if (x <= mid) return query(left(sta, end), x) + p[p[end].rs].cnt - p[p[sta].rs].cnt;
    else return query(right(sta, end), x);
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &num[i]), insert(root[i], root[i - 1], 1, maxn << 1, i + num[i]);
        if (i - 1)ans = ans + query(root[max(i - num[i], 0)], root[i - 1], 1, maxn << 1, 1 + i);
    }
    printf("%lld\n", ans);
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值