题目链接
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 << ' '; return 0; }