bzoj3110: [Zjoi2013]K大數查詢(主席樹+樹狀陣列)

Hanks_o發表於2018-04-11

題目傳送門

解法:
據說有很多種寫法。
我寫的主席樹套樹狀陣列。
修改的話就差分一下就好啦。
位置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;
}

相關文章