数据结构(树套树):ZJOI 2013 K大数查询

  

  有几个点卡常数……

  发现若第一维为位置,第二维为大小,那么修改时第一维修改区间,查询时第一维查询区间,必须挂标记。而这种情况下标记很抽象,而且Push_down不是O(1)的,并不可行。
  那要怎么做呢?不妨交换一下,第一维为大小,第二维为位置,在第二维中挂标记,这样Push_down就是O(1)的了。
  做完这道题,我最大的启发就是:树套树不适于在第一维挂标记,因为标记的维度会是一维的,根本不好维护。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <ctime>
 5 using namespace std;
 6 const int maxn=50010;
 7 const int maxm=20000010;
 8 int rt[maxn*4],tot,n,m;
 9 int ch[maxm][2],sum[maxm],tag[maxm];
10 
11 void Push_up(int x){
12     sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
13 }
14 
15 void Add(int &x,int l,int r,int d){
16     if(!x)x=++tot;
17     sum[x]+=(r-l+1)*d;
18     tag[x]+=d;
19 }
20 
21 void Push_down(int x,int l,int r){
22     if(tag[x]&&l!=r){
23         int mid=(l+r)>>1;
24         if(!ch[x][0])ch[x][0]=++tot;
25         if(!ch[x][1])ch[x][1]=++tot;
26         sum[ch[x][0]]+=(mid-l+1)*tag[x];tag[ch[x][0]]+=tag[x];
27         sum[ch[x][1]]+=(r-mid)*tag[x];tag[ch[x][1]]+=tag[x];
28         tag[x]=0;
29     }
30 }
31 
32 void Update(int &x,int l,int r,int a,int b){
33     if(!x)x=++tot;
34     Push_down(x,l,r);
35     if(l>=a&&r<=b){sum[x]+=r-l+1;tag[x]+=1;return;}
36     int mid=(l+r)>>1;
37     if(mid>=a)Update(ch[x][0],l,mid,a,b);
38     if(mid<b)Update(ch[x][1],mid+1,r,a,b);
39     Push_up(x);
40 }
41 
42 void Modify(int x,int l,int r,int g,int a,int b){
43     Update(rt[x],1,n,a,b);
44     if(l==r)return;
45     int mid=(l+r)>>1;
46     if(mid>=g)Modify(x<<1,l,mid,g,a,b);
47     else Modify(x<<1|1,mid+1,r,g,a,b);
48 }
49 
50 int Query(int x,int l,int r,int a,int b){
51     if(!x)return 0;
52     Push_down(x,l,r);
53     if(l>=a&&r<=b)return sum[x];
54     int mid=(l+r)>>1,ret=0;
55     if(mid>=a)ret=Query(ch[x][0],l,mid,a,b);
56     if(mid<b)ret+=Query(ch[x][1],mid+1,r,a,b);
57     return ret;
58 }
59 
60 int Solve(int l,int r,int k){
61     int lo=1,hi=n,p=1;
62     while(lo<hi){
63         int mid=(lo+hi)>>1,tmp;
64         tmp=Query(rt[p<<1],1,n,l,r);
65         if(tmp>=k)hi=mid,p<<=1;
66         else lo=mid+1,p=p<<1|1,k-=tmp;
67     }
68     return hi;
69 }
70 
71 int op,a,b,c;
72 int main(){
73 #ifndef ONLINE_JUDGE
74     freopen("zjoi13_sequence.in","r",stdin);
75     freopen("zjoi13_sequence.out","w",stdout);
76 #endif    
77     scanf("%d%d",&n,&m);
78     while(m--){
79         scanf("%d%d%d%d",&op,&a,&b,&c);
80         if(op==1)Modify(1,1,n,n-c+1,a,b);
81         else printf("%d
",n-Solve(a,b,c)+1);
82     }
83     //printf("%.2f
",(double)clock()/CLOCKS_PER_SEC);
84     return 0;
85 }

  然后就是喜闻乐见的整体二分,很好理解。

  有些地方没有缩行,打丑了,理论上60行足矣,这就是整体二分的威力!

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 using namespace std;
  5 const int maxn=50010;
  6 struct Node{
  7     int tp,l,r,k,id,tmp;
  8 }p[maxn],tmp[maxn];
  9 
 10 int b0[maxn],b1[maxn];
 11 int v0[maxn],v1[maxn];
 12 int ans[maxn],n,Q,tim,cntQ;
 13 
 14 void Add0(int x,int d){
 15     while(x<=n){
 16         b0[x]=v0[x]==tim?b0[x]+d:d;
 17         v0[x]=tim;
 18         x+=x&(-x);
 19     }
 20 }
 21 
 22 void Add1(int x,int d){
 23     while(x<=n){
 24         b1[x]=v1[x]==tim?b1[x]+d:d;
 25         v1[x]=tim;
 26         x+=x&(-x);
 27     }
 28 }
 29 
 30 void Update(int l,int r,int d){
 31     Add0(l,d);
 32     Add0(r+1,-d);
 33     Add1(l,-(l-1)*d);
 34     Add1(r+1,r*d);
 35 }
 36 
 37 int Query(int x){
 38     int ret=0;
 39     for(int i=x;i;i-=i&(-i))
 40         ret+=v0[i]==tim?b0[i]:0;
 41     ret*=x;
 42     for(int i=x;i;i-=i&(-i))
 43         ret+=v1[i]==tim?b1[i]:0;
 44     return ret;    
 45 }
 46 
 47 int Que(int l,int r){
 48     return Query(r)-Query(l-1);
 49 } 
 50 
 51 void Solve(int h,int t,int l,int r){
 52     if(h>t)return;
 53     if(l==r){
 54         for(int i=h;i<=t;i++)
 55             ans[p[i].id]=l;
 56         return;    
 57     }
 58     int mid=(l+r)>>1;
 59     int ct1=h,ct2=h;++tim;
 60     for(int i=h;i<=t;i++){
 61         if(p[i].tp==1){
 62             if(p[i].k>mid)continue;
 63             Update(p[i].l,p[i].r,1);
 64             ct2+=1;
 65         }
 66         else{
 67             p[i].tmp=Que(p[i].l,p[i].r);
 68             if(p[i].tmp>=p[i].k)ct2+=1;
 69         }
 70     }
 71     for(int i=h;i<=t;i++){
 72         if(p[i].tp==1){
 73             if(p[i].k>mid)tmp[ct2++]=p[i];
 74             else tmp[ct1++]=p[i];
 75         }
 76         else{
 77             if(p[i].tmp>=p[i].k)tmp[ct1++]=p[i];
 78             else p[i].k-=p[i].tmp,tmp[ct2++]=p[i];
 79         }
 80     }
 81     for(int i=h;i<=t;i++)p[i]=tmp[i];
 82     Solve(h,ct1-1,l,mid);Solve(ct1,t,mid+1,r);
 83 }
 84 
 85 int main(){
 86 #ifndef ONLINE_JUDGE
 87     freopen("zjoi13_sequence.in","r",stdin);
 88     freopen("zjoi13_sequence.out","w",stdout);
 89 #endif
 90     scanf("%d%d",&n,&Q);
 91     for(int i=1;i<=Q;i++){
 92         scanf("%d%d",&p[i].tp,&p[i].l);
 93         scanf("%d%d",&p[i].r,&p[i].k);
 94         if(p[i].tp==2){
 95             p[i].id=++cntQ;
 96             p[i].k=Que(p[i].l,p[i].r)-p[i].k+1;
 97         }
 98         else
 99             Update(p[i].l,p[i].r,1);
100     }
101     
102     Solve(1,Q,1,n);
103     
104     for(int i=1;i<=cntQ;i++)
105         printf("%d
",ans[i]);
106     return 0;
107 }
原文地址:https://www.cnblogs.com/TenderRun/p/5616464.html