题目传送门
。
解法:
据说有很多种写法。
我写的主席树套树状数组。
修改的话就差分一下就好啦。
位置l加上影响,位置r+1消除影响。
用树状数组来求每个点有哪些数。
每个点的信息相当于求1~这个点的前缀和(差分)
那么我们怎么求区间呢。
相当于求前缀和的前缀和。
如果要求1到i的信息。
某个位置为j。
a[j]对与i的贡献就为a[j]*(i-j+1)
那么我们把i+1提出来。剩下a[j]*(-j)
我们可以维护a[j]的前缀和。
然后再维护a[j]*j的前缀和。
然后用a[j]的前缀和*(i+1)减去a[j]*j的前缀和即可。
其他写法我都不会我好菜
代码实现:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
struct node {int lc,rc,c;ll s;}t[5100000];int cnt,rt[110000];
void build(int &u,int l,int r,int p,int c,int s) {
if(u==0)u=++cnt;t[u].c+=c;t[u].s+=ll(c*s);
if(l==r)return ;int mid=(l+r)/2;
if(p<=mid)build(t[u].lc,l,mid,p,c,s);
else build(t[u].rc,mid+1,r,p,c,s);
}
int lowbit(int x) {return x&-x;}int n;
void change(int x,int p,int c) {int X=x;while(x<=n) {build(rt[x],0,2*n,p,c,X);x+=lowbit(x);}}
bool v[110000];int ust[5100000];
void turn(int x,int c) {
while(x>0) {
if(c==0)ust[x]=rt[x];
else if(c==1&&v[x]==false)ust[x]=t[ust[x]].lc;
else if(v[x]==false)ust[x]=t[ust[x]].rc;
v[x]=true;x-=lowbit(x);
}
}
ll find_sum(int x) {
ll ans=0;
while(x>0) {v[x]=false;ans+=t[t[ust[x]].rc].c;x-=lowbit(x);}
return ans;
}
ll find_s(int x) {
ll ans=0;
while(x>0) {v[x]=false;ans+=t[t[ust[x]].rc].s;x-=lowbit(x);}
return ans;
}
ll get_sum(int x) {return ll((x+1)*find_sum(x))-find_s(x);}
int find(int p1,int p2,int l,int r,int k) {
if(l==r)return l-n;int mid=(l+r)/2;
ll c=get_sum(p1)-get_sum(p2);
if(c>=k) {turn(p1,-1);turn(p2,-1);return find(p1,p2,mid+1,r,k);}
else {turn(p1,1);turn(p2,1);return find(p1,p2,l,mid,k-c);}
}
int main() {
int m;scanf("%d%d",&n,&m);
memset(v,false,sizeof(v));
for(int i=1;i<=m;i++) {
int t,a,b,c;scanf("%d%d%d%d",&t,&a,&b,&c);
if(t==1) {c+=n;change(a,c,1);change(b+1,c,-1);}
else {turn(b,0);turn(a-1,0);printf("%d\n",find(b,a-1,0,2*n,c));}
}
return 0;
}