bzoj3110 [Zjoi2013]K大数查询——线段树套线段树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3110
外层权值线段树套内层区间线段树;
之所以外层权值内层区间,是因为区间线段树需要标记下传,所以写在内层比较方便;
然而空间太大了,所以动态开点,大约每个外层线段树的点上有 logn 个内层线段树点;
最开始写的不知为何很快就WA:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const maxn=5e4+5,maxm=maxn*400; int n,m,rt[maxn<<2],ls[maxm],rs[maxm],cnt; int a[maxn],b[maxn],tp[maxn],c[maxn],tmp[maxn],tot; ll sum[maxm],lzy[maxm];//ll lzy 防止计算时爆int void pushdown(int x,int l,int r) { if(!lzy[x])return; if(!ls[x])ls[x]=++cnt; if(!rs[x])rs[x]=++cnt; int mid=((l+r)>>1); sum[ls[x]]+=(mid-l+1)*lzy[x]; sum[rs[x]]+=(r-mid)*lzy[x]; lzy[ls[x]]+=lzy[x]; lzy[rs[x]]+=lzy[x]; lzy[x]=0; } void add(int &x,int l,int r,int L,int R) { if(!x)x=++cnt; if(l>=L&&r<=R){sum[x]+=(r-l+1); lzy[x]++; return;} // pushdown(x,l,r); int mid=((l+r)>>1); if(mid>=L)add(ls[x],l,mid,L,R); if(mid<R)add(rs[x],mid+1,r,L,R); sum[x]=sum[ls[x]]+sum[rs[x]]; } void insert(int x,int l,int r,int tl,int tr,int c) { add(rt[x],1,n,tl,tr); if(l==r)return;// int mid=((l+r)>>1);//权值区间 if(c<=mid)insert(x<<1,l,mid,tl,tr,c); else insert(x<<1|1,mid+1,r,tl,tr,c); } ll ask(int x,int l,int r,int L,int R) { if(!x)return 0;// if(l>=L&&r<=R)return sum[x]; pushdown(x,l,r); int mid=((l+r)>>1); ll ret=0; // if(mid>=L)ret+=ask(x<<1,l,mid,L,R); // if(mid<R)ret+=ask(x<<1|1,mid+1,r,L,R); if(mid>=L)ret+=ask(ls[x],l,mid,L,R); if(mid<R)ret+=ask(rs[x],mid+1,r,L,R);//别写串了! return ret; } int query(int x,int l,int r,int L,int R,int k) { if(l==r)return l; ll tmp=ask(rt[x<<1|1],1,n,L,R),mid=((l+r)>>1);//查询第k大 if(tmp>=k)return query(x<<1|1,mid+1,r,L,R,k);// else return query(x<<1,l,mid,L,R,k-tmp);// } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&tp[i],&a[i],&b[i],&c[i]); if(tp[i]==1)tmp[++tot]=c[i]; } sort(tmp+1,tmp+n+1); tot=unique(tmp+1,tmp+n+1)-tmp-1; for(int i=1;i<=m;i++) { if(tp[i]==1) { int tt=lower_bound(tmp+1,tmp+tot+1,c[i])-tmp; insert(1,1,tot,a[i],b[i],tt); } else printf("%d\n",tmp[query(1,1,tot,a[i],b[i],c[i])]); } return 0; }
不会改了,所以模仿别人写了个标记永久化,然后WA惨;
因为不太熟悉标记永久化,没注意到更新以及查询时要注意不能重复,改了半天,终于好了...
学到了点标记永久化的细节。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const maxn=5e4+5,maxm=maxn*400; int n,m,rt[maxn<<2],ls[maxm],rs[maxm],cnt; int a[maxn],b[maxn],tp[maxn],c[maxn],tmp[maxn],tot,L,R; ll sum[maxm],lzy[maxm];//ll lzy 防止计算时爆int //void pushdown(int x,int l,int r) //{ // if(!lzy[x])return; // if(!ls[x])ls[x]=++cnt; // if(!rs[x])rs[x]=++cnt; // int mid=((l+r)>>1); // sum[ls[x]]+=(mid-l+1)*lzy[x]; sum[rs[x]]+=(r-mid)*lzy[x]; // lzy[ls[x]]+=lzy[x]; lzy[rs[x]]+=lzy[x]; // lzy[x]=0; //} void add(int &x,int l,int r,int L,int R) { if(!x)x=++cnt; if(l==L&&r==R){/*sum[x]+=(r-l+1);*/ lzy[x]++; return;}//== sum[x]+=(R-L+1); // pushdown(x,l,r); int mid=((l+r)>>1); // if(mid>=L)add(ls[x],l,mid,L,R);//会重复计算sum!(因为标记永久化) // if(mid<R)add(rs[x],mid+1,r,L,R); // sum[x]=sum[ls[x]]+sum[rs[x]]; if(mid<L)add(rs[x],mid+1,r,L,R); else if(mid>=R)add(ls[x],l,mid,L,R); else add(ls[x],l,mid,L,mid),add(rs[x],mid+1,r,mid+1,R); } ll ask(int x,int l,int r,int L,int R) { if(!x)return 0;// ll ret=lzy[x]*(R-L+1); if(l==L&&r==R)return ret+sum[x];// // pushdown(x,l,r); int mid=((l+r)>>1); // if(mid>=L)ret+=ask(x<<1,l,mid,L,R); // if(mid<R)ret+=ask(x<<1|1,mid+1,r,L,R);//别把ls,rs和x<<1,x<<1|1写串了! // if(mid>=L)ret+=ask(ls[x],l,mid); // if(mid<R)ret+=ask(rs[x],mid+1,r); //会重复计算!! // printf("ret=%lld\n",ret); if(mid<L)return ret+ask(rs[x],mid+1,r,L,R); else if(mid>=R)return ret+ask(ls[x],l,mid,L,R); else return ret+ask(ls[x],l,mid,L,mid)+ask(rs[x],mid+1,r,mid+1,R); } int query(int x,int l,int r,int k) { if(l==r)return l; ll tmp=ask(rt[x<<1|1],1,n,L,R),mid=((l+r)>>1);//查询第k大 if(tmp>=k)return query(x<<1|1,mid+1,r,k);// else return query(x<<1,l,mid,k-tmp);// } void insert(int x,int l,int r,int c) { add(rt[x],1,n,L,R); if(l==r)return;// int mid=((l+r)>>1);//权值区间 if(c<=mid)insert(x<<1,l,mid,c); else insert(x<<1|1,mid+1,r,c); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&tp[i],&a[i],&b[i],&c[i]); if(tp[i]==1)tmp[++tot]=c[i]; } sort(tmp+1,tmp+n+1); tot=unique(tmp+1,tmp+n+1)-tmp-1; for(int i=1;i<=m;i++) { L=a[i],R=b[i]; if(tp[i]==1) { int tt=lower_bound(tmp+1,tmp+tot+1,c[i])-tmp; insert(1,1,tot,tt); } else printf("%d\n",tmp[query(1,1,tot,c[i])]); } return 0; }