链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417
题意:
给你段长为n的序列,有q个询问,每次询问区间[l.r]内有多少个数小于等于k
思路:
之前用分块写过类似的,不过为了练习下主席树,这里用主席树写了下。思路很简单
离线离散化处理下,每次插入一个数num时,在主席树上下标num+1,这样每次询问[l,r]中有多少个小于k的数的时候,我们只要找下标【1,k】的区间第R次修改后的总和减去第L-1次修改后的总值就可以得到了
实现代码:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int M = 2e5+10; int ls[M*40],rs[M*40],sum[M*40],a[M],b[M],root[M]; int idx; void build(int &k,int l,int r){ k = ++idx; sum[k] = 0; if(l == r) return ; int mid = (l + r) >> 1; build(ls[k],l,mid); build(rs[k],mid+1,r); } void update(int old,int &k,int l,int r,int p,int c){ k = ++idx; ls[k] = ls[old]; rs[k] = rs[old]; sum[k] = sum[old] + c; if(l == r) return ; int mid = (l + r) >> 1; if(p <= mid) update(ls[old],ls[k],l,mid,p,c); else update(rs[old],rs[k],mid+1,r,p,c); } int query(int old,int k,int L,int R,int l,int r){ if(L <= l&&R >= r) return sum[k] - sum[old]; int mid = (l + r) >> 1; int ret = 0; if(L <= mid) ret += query(ls[old],ls[k],L,R,l,mid); if(R > mid) ret += query(rs[old],rs[k],L,R,mid+1,r); return ret; } int l[M],r[M],x[M],cnt; int Find(int x){ int num = lower_bound(b+1,b+1+cnt,x)-b; return num; } int main() { int t,q,n,cas=1; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&q); for(int i = 1;i <= n;i ++) scanf("%d",&a[i]),b[i] = a[i]; for(int i = 1;i <= q;i ++) scanf("%d%d%d",&l[i],&r[i],&x[i]),b[i+n]=x[i]; sort(b+1,b+1+n+q); cnt = unique(b+1,b+1+n+q)-b-1; for(int i = 1;i <= n;i++){ int fx = Find(a[i]); update(root[i-1],root[i],1,cnt,fx,1); } printf("Case %d:\n",cas++); for(int i = 1;i <= q;i ++){ int fx = Find(x[i]); printf("%d\n",query(root[l[i]],root[r[i]+1],1,fx,1,cnt)); } } return 0; }
原文地址:https://www.cnblogs.com/kls123/p/9568553.html
时间: 2024-10-06 18:56:58