3110: [Zjoi2013]K大数查询

来源:互联网 发布:java抢红包程序 编辑:程序博客网 时间:2024/05/17 06:37

题目大意,带该段的区间第k大。

注意,一个位置上可以有多个数的,更改操作是增加新数,而不是覆盖。

备用知识:标记永久化点击打开链接(不用的话应该也可以,但空间会大)

然后就上权值线段树套区间线段树。

不修改的区间第k小是例题(类似二分),树套树可以解决修改的问题。

我们可以c+n+1转正,然后把求区间第k大变成(len-k+1)小。

答案再减回来就行了。

要用long long

code:

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#define LL long longusing namespace std;int n,m,N;struct node{    int lc,rc;    LL c,u;}tr[20200010];int trlen=0,root[200010];void update(int &x,int l,int r,int fl,int fr,LL c){    if(!x) x=++trlen;    if(l==fl&&r==fr){tr[x].u+=c;tr[x].c+=c*(r-l+1);return;}    int mid=(l+r)/2;    if(fr<=mid) update(tr[x].lc,l,mid,fl,fr,c);    else if(fl>mid) update(tr[x].rc,mid+1,r,fl,fr,c);    else update(tr[x].lc,l,mid,fl,mid,c),update(tr[x].rc,mid+1,r,mid+1,fr,c);    tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c+(r-l+1)*tr[x].u;}LL findans(int x,int l,int r,int fl,int fr,LL u){    if(!x) return u*(LL)(fr-fl+1);    if(l==fl&&fr==r) return tr[x].c+(LL)(r-l+1)*u;    int mid=(l+r)/2;    if(fr<=mid) return findans(tr[x].lc,l,mid,fl,fr,u+tr[x].u);    if(fl>mid) return findans(tr[x].rc,mid+1,r,fl,fr,u+tr[x].u);    return findans(tr[x].lc,l,mid,fl,mid,u+tr[x].u)+findans(tr[x].rc,mid+1,r,mid+1,fr,u+tr[x].u);}int bt(int l,int r){    int x=++trlen;    if(l!=r)    {        int mid=(l+r)/2;        tr[x].lc=bt(l,mid);        tr[x].rc=bt(mid+1,r);    }    return x;}void change(int x,int l,int r,int fl,int fr,int k,LL c){    update(root[x],1,n,fl,fr,c);    if(l==r) return;    int mid=(l+r)/2;    if(k<=mid) change(tr[x].lc,l,mid,fl,fr,k,c);    else change(tr[x].rc,mid+1,r,fl,fr,k,c);}int solve(int x,int l,int r,int fl,int fr,LL k){    if(l==r) return l;    int mid=(l+r)/2;LL sum=findans(root[tr[x].lc],1,n,fl,fr,0LL);    if(k<=sum) return solve(tr[x].lc,l,mid,fl,fr,k);    else return solve(tr[x].rc,mid+1,r,fl,fr,k-sum);}int findnum(int l,int r,LL c){    LL k=(findans(root[1],1,n,l,r,0)-c+1);    if(k<=0) k=1;    return solve(1,1,N,l,r,k)-n-1;}int main(){    scanf("%d %d",&n,&m);    N=2*n+1;bt(1,N);    for(int i=1;i<=m;i++)    {        int tmp,x,y,c;scanf("%d %d %d %d",&tmp,&x,&y,&c);        if(tmp==1) change(1,1,N,x,y,c+n+1,1LL);        else printf("%d\n",findnum(x,y,(LL)c));    }}

数据生成器:

#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<iostream>#define LL long longusing namespace std;struct node{    int lc,rc;LL c,u;}tr[400010];int trlen=0;int n,m,root=0;void update(int &x,int l,int r,int fl,int fr,LL c){    if(!x) x=++trlen;    if(l==fl&&r==fr){tr[x].u+=c;tr[x].c+=c*(r-l+1);return;}    int mid=(l+r)/2;    if(fr<=mid) update(tr[x].lc,l,mid,fl,fr,c);    else if(fl>mid) update(tr[x].rc,mid+1,r,fl,fr,c);    else update(tr[x].lc,l,mid,fl,mid,c),update(tr[x].rc,mid+1,r,mid+1,fr,c);    tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c+(r-l+1)*tr[x].u;}LL findans(int x,int l,int r,int fl,int fr,LL u){    if(!x) return u*(LL)(fr-fl+1);    if(l==fl&&fr==r) return tr[x].c+(LL)(r-l+1)*u;    int mid=(l+r)/2;    if(fr<=mid) return findans(tr[x].lc,l,mid,fl,fr,u+tr[x].u);    if(fl>mid) return findans(tr[x].rc,mid+1,r,fl,fr,u+tr[x].u);    return findans(tr[x].lc,l,mid,fl,mid,u+tr[x].u)+findans(tr[x].rc,mid+1,r,mid+1,fr,u+tr[x].u);}int main(){srand(time(0));int n=7000,m=5000;printf("%d %d\n",n,m);for(int i=1;i<=m;i++){int l=rand()%n+1,r=rand()%n+1,c=(rand()%9000-rand()%9000)%n;if(l>r) swap(l,r);int tmp=rand()%2+1;if(tmp==1) update(root,1,n,l,r,1LL);else{LL TMP=findans(root,1,n,l,r,0);if(TMP==0) tmp=1;else c=rand()%TMP+1;}printf("%d %d %d %d\n",tmp,l,r,c);}}