题目大意,带该段的区间第k大。
注意,一个位置上可以有多个数的,更改操作是增加新数,而不是覆盖。
备用知识:标记永久化点击打开链接(不用的话应该也可以,但空间会大)
然后就上权值线段树套区间线段树。
不修改的区间第k小是例题(类似二分),树套树可以解决修改的问题。
我们可以c+n+1转正,然后把求区间第k大变成(len-k+1)小。
答案再减回来就行了。
要用long long
code:
- #include<cstdio>
- #include<cstdlib>
- #include<cstring>
- #include<iostream>
- #define LL long long
- using 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 long
- using 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);
- }
- }