CF961E Tufurama

题目链接:https://codeforces.com/contest/961/problem/E 

题意:

  给你一个长度为 N 的数组 A

  问 j < i && a[i] >= j  && a[j] >= i 的 (i , j) 有多少对

分析:

  权值线段树

  其实这是道主席树裸题 , 但我想介绍一种权值线段树的做法

  根据条件我们可以把问题转换成 [1 , min(a[i] , i - 1)] 内有多少个 j 使得 a[j] >= i

  即给你一个数组 A , 以及 N 个询问Q , 每次问你 [L , R] 内大于等于 X 的数有多少个

  我个人习惯求 [L , R] 内小于 X 的数有多少个

  那么这样每个询问对答案的贡献就是 R - L + 1 - cnt

  首先我们可以将询问储存下来:

  对于第 i 个询问 , 它的 L = 1 , R = min(a[i] , i  - 1) , X = i

  然后对所有询问按照 X 的大小升序排序 

  对于数组 A , 我们记录每个数下标的位置后 , 也按照ai的大小升序排序

  接下来我们就可以开始枚举询问了

  对于第 i 个询问 , 我们把数组A中小于Q[i].X的数的下标对应权值线段树的位置 + 1

  表示原数组中这一个位置有 1 个数是小于 Q[i].X的

  待所有A中小于 Q[i].X的数加入到权值线段树中  , 我们再查询 [L , R] 的区间和

  此时查询的结果 = 小于Q[i].X的数的个数,因为我们只把小于Q[i].X的数插入了权值线段树中

  所以查询的结果就是区间内小于 Q[i].X 的数的个数(算是一种小套路吧)

#include<bits/stdc++.h>
#define ios std::ios::sync_with_stdio(false)
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define ll long long
#define int long long
#define il inline
using namespace std;
struct Tree
{
    ll l,r,sum,lazy,maxn,minn;
} tree[1000000];
il void push_up(ll rt)
{
    tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
    tree[rt].maxn=max(tree[rt<<1].maxn,tree[rt<<1|1].maxn);
    tree[rt].minn=min(tree[rt<<1].minn,tree[rt<<1|1].minn);
}
il void push_down(ll rt , ll length)
{
    if(tree[rt].lazy)
    {
        tree[rt<<1].lazy+=tree[rt].lazy;
        tree[rt<<1|1].lazy+=tree[rt].lazy;
        tree[rt<<1].sum+=(length-(length>>1))*tree[rt].lazy;
        tree[rt<<1|1].sum+=(length>>1)*tree[rt].lazy;
        tree[rt<<1].minn+=tree[rt].lazy;
        tree[rt<<1|1].minn+=tree[rt].lazy;
        tree[rt<<1].maxn+=tree[rt].lazy;
        tree[rt<<1|1].maxn+=tree[rt].lazy;
        tree[rt].lazy=0;
    }
}
il void build(ll l , ll r , ll rt , ll *aa)
{
    tree[rt].lazy=0;
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r)
    {
        tree[rt].sum=aa[l];
        tree[rt].minn=tree[rt].sum;
        tree[rt].maxn=tree[rt].sum;
        return;
    }
    ll mid=(l+r)>>1;
    build(l,mid,rt<<1,aa);
    build(mid+1,r,rt<<1|1,aa);
    push_up(rt);
}
il void update_range(ll L , ll R , ll key , ll rt)
{
    if(tree[rt].r<L||tree[rt].l>R)return;
    if(L<=tree[rt].l&&R>=tree[rt].r)
    {
        tree[rt].sum+=(tree[rt].r-tree[rt].l+1)*key;
        tree[rt].minn+=key;
        tree[rt].maxn+=key;
        tree[rt].lazy+=key;
        return;
    }
    push_down(rt,tree[rt].r-tree[rt].l+1);
    ll mid=(tree[rt].r+tree[rt].l)>>1;
    if(L<=mid)update_range(L,R,key,rt << 1);
    if(R>mid)update_range(L,R,key,rt << 1 | 1);
    push_up(rt);
}
il ll query_range(ll L, ll R, ll rt)
{
    if(L<=tree[rt].l&&R>=tree[rt].r)
    {
        return tree[rt].sum;
    }
    push_down(rt,tree[rt].r-tree[rt].l+1);
    ll mid=(tree[rt].r+tree[rt].l)>>1;
    ll ans=0;
    if(L<=mid)ans+=query_range(L,R,rt << 1);
    if(R>mid)ans+=query_range(L,R,rt << 1 | 1);
    return ans;
}

const int N = 2e5 + 10;
int n , k , ans;
ll aa[N];
struct node
{
    int x , id;
    int l , r;
    bool operator < (const node &a) const
    {
        return x < a.x;
    }
} a[N] , q[N];
bool cmp(node a , node b){
    return a.id < b.id;
}
signed main()
{
    ios;
    cin.tie(0);
    int n;
    cin >> n;
    rep(i , 1 , n)
    {
        cin >> a[i].x , a[i].id = i;
        q[i].x = q[i].id = i;
        q[i].l = 1 , q[i].r = min(a[i].x , i - 1);
    }
    sort(a + 1 , a + 1 + n);
    sort(q + 1 , q + 1 + n);
    int now = 1;
    build(1 , n , 1 , aa);
    rep(i , 1 , n)
    {
        int l = q[i].l , r = q[i].r , x = q[i].x;
        while(a[now].x < x && now <= n)
        {
            update_range(a[now].id , a[now].id , 1 , 1);
            now ++ ;
        }
        ans += r - l + 1 - query_range(l , r , 1);
    }
    cout << ans << '\n';
    return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/StarRoadTang/p/12655526.html