题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=3110
题意:有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置插入一个数c;如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。(题目说对于操作1,abs(c)<=N,其实数据中没有c为负数的)
思路:两层线段树,第一层以插入的数字建树,称这个线段树为A,第二层以N个位置建树,称这种线段树为B。A的每个节点都有一个B节点。比如所有插入数字范围为[1,8],有8个位置,N=8。某一次在区间[1,4]插入数字2,我们从A中看到,包含数字2的节点为[1,8],[1,4],[1,2],[2,2],更新时A的每个节点对应的B都插入一次区间[1,4]。统计区间第K大值时,假设当前为A的某个节点p,我们首先在p的右子树中统计总个数是否大于K(这里其实就是在p的右孩子对应的B节点统计总个数),若大于则在右子树查找,否则在左子树。
struct node1
{
int L,R,t;
};
struct node2
{
int L,R,det;
i64 sum;
};
node1 a1[N];
node2 a2[N];
int e1,e2;
int n,m;
void pushDown(int t)
{
if(!a2[t].det) return;
if(!a2[t].L) a2[t].L=++e2;
if(!a2[t].R) a2[t].R=++e2;
a2[a2[t].L].det+=a2[t].det;
a2[a2[t].R].det+=a2[t].det;
a2[t].det=0;
}
void cover(int t,int L,int R,int l,int r)
{
if(L>R) return;
if(L==l&&R==r)
{
a2[t].det++;
return;
}
pushDown(t);
int mid=(L+R)>>1;
if(r<=mid)
{
if(!a2[t].L) a2[t].L=++e2;
cover(a2[t].L,L,mid,l,r);
}
else if(l>mid)
{
if(!a2[t].R) a2[t].R=++e2;
cover(a2[t].R,mid+1,R,l,r);
}
else
{
if(!a2[t].L) a2[t].L=++e2;
if(!a2[t].R) a2[t].R=++e2;
cover(a2[t].L,L,mid,l,mid);
cover(a2[t].R,mid+1,R,mid+1,r);
}
a2[t].sum=0;
int x=a2[t].L,y=a2[t].R;
if(x) a2[t].sum+=a2[x].det*(mid-L+1)+a2[x].sum;
if(y) a2[t].sum+=a2[y].det*(R-mid)+a2[y].sum;
}
void build(int t,int L,int R,int pos,int l,int r)
{
if(L>R) return;
if(!a1[t].t) a1[t].t=++e2;
cover(a1[t].t,1,n,l,r);
if(L==R) return;
int mid=(L+R)>>1;
if(pos<=mid)
{
if(!a1[t].L) a1[t].L=++e1;
build(a1[t].L,L,mid,pos,l,r);
}
else
{
if(!a1[t].R) a1[t].R=++e1;
build(a1[t].R,mid+1,R,pos,l,r);
}
}
int find(int t,int L,int R,int l,int r)
{
if(L>R) return 0;
if(L==l&&R==r) return a2[t].det*(r-l+1)+a2[t].sum;
pushDown(t);
int mid=(L+R)>>1;
i64 ans=0;
if(r<=mid) ans=find(a2[t].L,L,mid,l,r);
else if(l>mid) ans=find(a2[t].R,mid+1,R,l,r);
else
{
if(a2[t].L) ans+=find(a2[t].L,L,mid,l,mid);
if(a2[t].R) ans+=find(a2[t].R,mid+1,R,mid+1,r);
}
a2[t].sum=0;
int x=a2[t].L,y=a2[t].R;
if(x) a2[t].sum+=a2[x].det*(mid-L+1)+a2[x].sum;
if(y) a2[t].sum+=a2[y].det*(R-mid)+a2[y].sum;
return ans;
}
int query(int t,int L,int R,int K,int l,int r)
{
if(L>R) return 0;
if(L==R) return L;
int mid=(L+R)>>1;
int temp;
if(a1[t].R)
{
temp=find(a1[a1[t].R].t,1,n,l,r);
if(temp>=K) return query(a1[t].R,mid+1,R,K,l,r);
K-=temp;
}
return query(a1[t].L,L,mid,K,l,r);
}
int main()
{
RD(n,m);
int op,a,b,c;
while(m--)
{
RD(op); RD(a,b,c);
if(op==1) build(0,0,n,c,a,b);
else PR(query(0,0,n,c,a,b));
}
}