bzoj3110: [Zjoi2013]K大数查询(主席树+树状数组)

17 篇文章 0 订阅
10 篇文章 0 订阅

题目传送门

解法:
据说有很多种写法。
我写的主席树套树状数组。
修改的话就差分一下就好啦。
位置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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值