zkw线段树两题

普通的线段树也能写,只是练习一下zkw线段树。


POJ 3468

// POJ 3468
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=100000+10;

int n,bit,q;
ll sum[maxn<<2],add[maxn<<2];

// 带输入的init
void init(){
    bit=1;
    while (bit<=n+1)    bit<<=1;
    memset(add,0,sizeof(add));
    for (int i=bit+1;i<=bit+n;i++)  scanf("%lld",&sum[i]);
    sum[bit]=0;
    for (int i=bit+n+1;i<=bit+bit-1;i++)   sum[i]=0;
    for (int i=bit-1;i;i--) sum[i]=sum[i<<1]+sum[i<<1|1];
}

// 区间求和
ll query(int l,int r){
    int ln=0,rn=0,x=1; // 左右分支已加的点数和每树元素个数
    ll ans=0;
    for (l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1,x<<=1){
        // 先读取标记的更新
        if (add[l]) ans+=add[l]*ln;
        if (add[r]) ans+=add[r]*rn;
        // 再常规求和
        if (~l&1)   ans+=sum[l^1]+x*add[l^1],ln+=x;
        if (r&1)    ans+=sum[r^1]+x*add[r^1],rn+=x;
    }
    // 处理同层的情况
    if (add[l]) ans+=add[l]*ln;
    if (add[r]) ans+=add[r]*rn;
    ln+=rn;
    l>>=1;
    // 处理上层的情况
    for (;l^1;l>>=1){
        if (add[l])
            ans+=add[l]*ln;
    }
    return ans;
}

// 区间加v
void increase(int l,int r,int v){
    int ln=0,rn=0,x=1;
    for (l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1,x<<=1){
        // 先更新sum
        sum[l]+=1ll*v*ln;
        sum[r]+=1ll*v*rn;
        // 再更新add
        if (~l&1)   add[l^1]+=v,ln+=x;
        if (r&1)    add[r^1]+=v,rn+=x;
    }
    // 处理同层的情况
    sum[l]+=1ll*v*ln;
    sum[r]+=1ll*v*rn;
    ln+=rn;
    l>>=1;
    // 处理上层的情况
    for (;l^1;l>>=1)   sum[l]+=1ll*v*ln;
}

char op[2];

int main(){
    scanf("%d %d",&n,&q);
    init();
    while (q--){
        scanf("%s",op);
        if (op[0]=='Q'){
            int a,b;
            scanf("%d %d",&a,&b);
            printf("%lld\n",query(a,b));
        }
        else{
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            increase(a,b,c);
        }
    }
}

cf 52c

// cf 52c
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;

int bit,n,q;
ll tree[maxn<<2];
char str[100];

void build(int n){
    for (bit=1;bit<=n+1;bit<<=1);
    for (int i=bit+1;i<=bit+n;i++)    scanf("%lld",&tree[i]);
    for (int i=bit-1;i;i--) tree[i]=min(tree[i<<1],tree[i<<1|1]);
    for (int i=bit+n;i;i--)   tree[i]=tree[i]-tree[i >> 1];
}

void update(int l,int r,int x){
    ll tmp;
    for (l=l+bit-1,r=r+bit+1;l^r^1;l>>=1,r>>=1){
        if (~l&1)   tree[l^1]+=x;
        if (r&1)    tree[r^1]+=x;
        tmp=min(tree[l],tree[l^1]),tree[l]-=tmp,tree[l^1]-=tmp,tree[l>>1]+=tmp;
        tmp=min(tree[r],tree[r^1]),tree[r]-=tmp,tree[r^1]-=tmp,tree[r>>1]+=tmp;
    }
    while(l>1)  tmp=min(tree[l],tree[l^1]),tree[l]-=tmp,tree[l^1]-=tmp,tree[l>>=1]+=tmp;
}

ll query(int l,int r){
    l+=bit,r+=bit;
    ll lans=0,rans=0;
    if (l!=r){
        for (;l^r^1;l>>=1,r>>=1){
            lans+=tree[l],rans+=tree[r];
            if(~l&1)    lans=min(lans,tree[l^1]);
            if(r&1)   rans=min(rans,tree[r^1]);
        }
    }
    ll ans=min(lans+tree[l],rans+tree[r]);
    while (l>1) ans+=tree[l>>=1];
    return ans;
}

vector <int> cal(char s[],int len){
    vector <int> vec;
    bool flag=0;
    int ans=0;
    for (int i=0;i<len;i++){
        if (s[i]==' '||s[i]=='\0'){
            if (flag)   ans=-ans;
            vec.push_back(ans);
            ans=0;
            flag=0;
        }
        else if (s[i]=='-'){
            flag=1;
        }
        else{
            ans=ans*10+s[i]-'0';
        }
    }
    if (flag)   ans=-ans;
    vec.push_back(ans);
    return vec;
}

int main(){
    scanf("%d",&n);
    build(n);
    scanf("%d",&q);
    getchar();
    while (q--){
        cin.getline(str,99);
        int len=strlen(str);
        vector <int> tmp=cal(str,len);
        int sz=tmp.size();
        if (sz==3){
            int l=tmp[0],r=tmp[1],v=tmp[2];
            l++,r++;
            if (l>r){
                update(1,r,v);
                update(l,n,v);
            }
            else    update(l,r,v);
        }
        else if (sz==2){
            int l=tmp[0],r=tmp[1];
            l++,r++;
            ll ans=1e18;
            if (l>r)    ans=min(query(1,r),query(l,n));
            else    ans=query(l,r);
            printf("%lld\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值