BZOJ - 3110 K大数查询 (树套树)

题目链接

权值线段树套区间线段树,权值线段树的每个结点存储该结点所表示的区间范围内的数在各个区间的分布情况,查询时在权值线段树上二分即可。复杂度$O(nlog^2n)$

注意区间线段树需要动态开点,并且标记要永久化,否则会TLE。

另外就是sum可能会爆int,需要用long long存储。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 typedef double db;
 5 const int N=5e4+10;
 6 #define lson (u<<1)
 7 #define rson (u<<1|1)
 8 #define mid ((l+r)>>1)
 9 int n,m,rt[N<<2],ls[N*200],rs[N*200],tot;
10 ll mk[N*200],sum[N*200];
11 int newnode() {int u=++tot; ls[u]=rs[u]=sum[u]=0; return u;}
12 void upd2(int L,int R,int x,int& u,int l=1,int r=n) {
13     if(!u)u=newnode();
14     if(l>=L&&r<=R) {sum[u]+=(r-l+1)*x; mk[u]+=x; return;}
15     if(l>R||r<L)return;
16     upd2(L,R,x,ls[u],l,mid),upd2(L,R,x,rs[u],mid+1,r);
17     sum[u]+=(ll)(min(r,R)-max(l,L)+1)*x;
18 }
19 ll qry2(int L,int R,int& u,int l=1,int r=n) {
20     if(l>=L&&r<=R)return sum[u];
21     if(l>R||r<L)return 0;
22     return qry2(L,R,ls[u],l,mid)+qry2(L,R,rs[u],mid+1,r)+(ll)(min(r,R)-max(l,L)+1)*mk[u];
23 }
24 void upd(int p,int L,int R,int u=1,int l=1,int r=n) {
25     upd2(L,R,1,rt[u]);
26     if(l==r)return;
27     p<=mid?upd(p,L,R,lson,l,mid):upd(p,L,R,rson,mid+1,r);
28 }
29 int qry(int L,int R,int k,int u=1,int l=1,int r=n) {
30     if(l==r)return l;
31     ll t=qry2(L,R,rt[rson]);
32     return k<=t?qry(L,R,k,rson,mid+1,r):qry(L,R,k-t,lson,l,mid);
33 }
34 int main() {
35     scanf("%d%d",&n,&m);
36     while(m--) {
37         int f,l,r,x;
38         scanf("%d%d%d%d",&f,&l,&r,&x);
39         if(f==1)upd(x,l,r);
40         else printf("%d\n",qry(l,r,x));
41     }
42     return 0;
43 }

 

posted @ 2019-04-10 19:45  jrltx  阅读(139)  评论(0编辑  收藏  举报