• HAOI2012高速公路——子区间计算


    题目:https://www.luogu.org/problemnew/show/P2221

    似乎按点来算贡献很方便,但我抱住一篇没有这样的题解磕了两天...

    以下转载:


    题意:维护一段数列 支持区间加和求区间所有子区间的和的和

    一看就知道要用线段树 于是用sum表示区间所有子区间的和的和 但是知道两个区间的sum并不能求出这两个区间并起来之后的sum 这时我们可以手玩一下

    sum(1 2 3 4)=(1)+(2)+(1 2)+(3)+(4)+(3 4)+(2 3)+(1 2 3)+(2 3 4)+(1 2 3 4)竖着写在纸上 就会发现除了sum(1 2)和sum(3 4)外 所有包含左区间右端点的区间和(rsum)恰被算了len(右区间)次 同理包含右区间左端点的区间和(lsum)被算了len(左区间)次

    于是 我们得到了sum(a b)=sum(a)+sum(b)+rsum(a)·len(b)+lsum(b)·len(a)

    那么如何维护lsum和rsum呢 显然lsum(a b)=lsum(a)+lsum(b)+区间和(a)*len(b) rsum同理

    至此维护操作已经没问题了 pushdown操作就简单很多了 当一个区间整体加a时 它的sum增加了a(n+2(n-1)+3(n-2)+...+n)也就是a((1+...+n)n-(1^2+...+(n-1)^2)-(1+...(n-1))) lsum rsum 同理 最后注意取GCD


    看样子很精妙,似乎也挺好写的;

    然而因为还是理解不到位,犯了众多细节的错误,调了两天,令人疯狂:

    1.线段树上的每个点是车站之间的一段,所以读入的n和r都要-1;

    2.需要另外记录纯粹的区间和(s),用于计算sum,lsum,rsum等等,而不是直接用sum一概计算;

    3.采用那样的build写法,竟然一度忘记在pushup里更新len,调了半天;

    *4.注意这个背景下的线段树与平常的线段树写法有所不同:

      平常的线段树在修改、查询时都会传下去一个目标区间,一般来说这个目标区间不用更改,判断只需看是否在其内部就可以;

      但这道题的计算是需要用到目标区间的,所以在把操作下放到子区间时,其目标区间也必须相应有所修改,否则在计算时就会大错特错;

      也正因此,必须进行判断:目标区间是完全在左儿子,还是完全在右儿子,还是在中间;不同的情况中目标区间的修改也有所不同。

    虽然这种写法似乎不是很优秀,但在线段树中痛苦挣扎的过程中,我认识到就算是线段树也不能不求甚解,靠习惯打板子;必须根据题目灵活调整,否则就会出现错误。

    错误版本1:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int const maxn=4e5+5;
    int n,m;
    struct N{
        int sum,lsum,rsum,len,lzy;
        int s;//!
    }t[maxn];
    int gcd(int x,int y){return y?gcd(y,x%y):x;}
    void yf(int x,int nn)//nn
    {
        int y=(1+nn)*nn/2;
        int a=x,b=y;
        if(a<b)swap(a,b);
        int c=gcd(a,b);
        printf("%d/%d
    ",x/c,y/c);
    }
    int cala(int x)//add
    {
        int nn=t[x].len;
        int n=nn-1;
        return (1+nn)*nn/2*nn-(1+n)*n/2-n*(n+1)*(n*2+1)/6;//
    }
    int calb(int x)//lsum,rsum add
    {
        int nn=t[x].len;
        return (1+nn)*nn/2;
    }
    void pushup(int x)
    {
        int ls=(x<<1),rs=(x<<1|1);
        t[x].len=t[x<<1].len+t[x<<1|1].len;
        t[x].s=t[x<<1].s+t[x<<1|1].s;
        t[x].sum=t[ls].sum+t[rs].sum+t[ls].rsum*t[rs].len+t[rs].sum*t[ls].len;
        t[x].lsum=t[ls].lsum+t[rs].lsum+t[ls].s*t[rs].len;//s
        t[x].rsum=t[ls].rsum+t[rs].rsum+t[rs].s*t[ls].len;
    }
    void build(int l,int r,int x)
    {
        if(l==r)
        {
            t[x].len=1;
            return;
        }
        int mid=((l+r)>>1);
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
        pushup(x);
    }
    void pushdown(int x)
    {
        if(t[x].lzy)
        {
            int v=t[x].lzy;t[x].lzy=0;
            int ls=(x<<1),rs=(x<<1|1);
            t[ls].s+=t[ls].len*v;
            t[ls].sum+=cala(ls)*v;
            t[ls].lsum+=calb(ls)*v;
            t[ls].rsum+=calb(ls)*v;
            t[ls].lzy+=v;
            t[rs].s+=t[rs].len*v;
            t[rs].sum+=cala(rs)*v;
            t[rs].lsum+=calb(rs)*v;
            t[rs].rsum+=calb(rs)*v;
            t[rs].lzy+=v;
        }
    }
    void add(int l,int r,int L,int R,int v,int x)
    {
        if(l>=L&&r<=R)
        {
            t[x].s+=t[x].len*v;
            t[x].sum+=cala(x)*v;
            t[x].lsum+=calb(x)*v;
            t[x].rsum+=calb(x)*v;
            t[x].lzy+=v;
            return;
        }
        pushdown(x);
        int mid=((l+r)>>1);
        if(L<=mid)add(l,mid,L,R,v,x<<1);
        if(R>mid)add(mid+1,r,L,R,v,x<<1|1);
        pushup(x);
    }
    int qs(int l,int r,int L,int R,int x)
    {
        if(l>=L&&r<=R)return t[x].s;
        pushdown(x);
        int ret=0;
        int mid=((l+r)>>1);
        if(L<=mid)ret+=qs(l,mid,L,R,x<<1);
        if(R>mid)ret+=qs(mid+1,r,L,R,x<<1|1);
        return ret;
    }
    int qls(int l,int r,int L,int R,int x)
    {
        if(l>=L&&r<=R)return t[x].lsum;
        pushdown(x);
        int mid=((l+r)>>1);
        if(L>mid)return qls(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return qls(l,mid,L,R,x<<1);
        return qls(mid+1,r,L,R,x<<1|1)+qls(l,mid,L,R,x<<1)+qs(l,mid,L,R,x<<1)*t[x<<1|1].len;
    }
    int qrs(int l,int r,int L,int R,int x)
    {
        if(l>=L&&r<=R)return t[x].rsum;
        pushdown(x);
        int mid=((l+r)>>1);
        if(L>mid)return qrs(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return qrs(l,mid,L,R,x<<1);
        return qrs(mid+1,r,L,R,x<<1|1)+qrs(l,mid,L,R,x<<1)+qs(mid+1,r,L,R,x<<1|1)*t[x<<1].len;
    }
    int query(int l,int r,int L,int R,int x)
    {
        if(l>=L&&r<=R)return t[x].sum;
        pushdown(x);
        int mid=((l+r)>>1);
        if(L>mid)return query(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return query(l,mid,L,R,x<<1);
        return query(mid+1,r,L,R,x<<1|1)+query(l,mid,L,R,x<<1)
    //        +qrs(l,mid,L,R,x<<1)*t[x<<1|1].len+qls(mid+1,r,L,R,x<<1|1)*t[x<<1].len;
            +qrs(l,mid,L,R,x<<1)*(R-mid)+qls(mid+1,r,L,R,x<<1|1)*(mid-L+1);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        n--;//
        build(1,n,1);
        for(int i=1,l,r,v;i<=m;i++)
        {
            char dc;
            cin>>dc;
            scanf("%d%d",&l,&r);//车站而非间距 
            r--;
            if(dc=='C')
            {
                scanf("%d",&v);
                add(1,n,l,r,v,1);
            }
            if(dc=='Q')
            {
                int k=query(1,n,l,r,1);
                yf(k,r-l+1);
            }
        }
        return 0;
    }

    错误版本2:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll const maxn=5e5+5;
    ll n,m;
    struct N{
        ll sum,lsum,rsum,len,lzy;
        ll s;//!
    }t[maxn];
    ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
    void yf(ll x,ll nn)//nn
    {
        ll y=(1+nn)*nn/2;
        ll a=x,b=y;
        if(a<b)swap(a,b);
        ll c=gcd(a,b);
        printf("%lld/%lld
    ",x/c,y/c);
    }
    ll cala(ll x)//add
    {
        ll nn=t[x].len;
        ll n=nn-1;
        return (1+nn)*nn/2*nn-(1+n)*n/2-n*(n+1)*(n*2+1)/6;//
    }
    ll calb(ll x)//lsum,rsum add
    {
        ll nn=t[x].len;
        return (1+nn)*nn/2;
    }
    void pushup(ll x)
    {
        ll ls=(x<<1),rs=(x<<1|1);
    //    t[x].len=t[x<<1].len+t[x<<1|1].len;
        t[x].s=t[x<<1].s+t[x<<1|1].s;
        t[x].sum=t[ls].sum+t[rs].sum+t[ls].rsum*t[rs].len+t[rs].lsum*t[ls].len;
        t[x].lsum=t[ls].lsum+t[rs].lsum+t[ls].s*t[rs].len;//s
        t[x].rsum=t[ls].rsum+t[rs].rsum+t[rs].s*t[ls].len;
    }
    void build(ll l,ll r,ll x)
    {
        t[x].len=r-l+1;
        if(l==r)
        {
    //        t[x].len=1;
            return;
        }
        ll mid=((l+r)>>1);
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
        pushup(x);
    }
    void pushdown(ll x)
    {
        if(t[x].lzy)
        {
            ll v=t[x].lzy;t[x].lzy=0;
            ll ls=(x<<1),rs=(x<<1|1);
            t[ls].s+=t[ls].len*v;
            t[ls].sum+=cala(ls)*v;
            t[ls].lsum+=calb(ls)*v;
            t[ls].rsum+=calb(ls)*v;
            t[ls].lzy+=v;
            t[rs].s+=t[rs].len*v;
            t[rs].sum+=cala(rs)*v;
            t[rs].lsum+=calb(rs)*v;
            t[rs].rsum+=calb(rs)*v;
            t[rs].lzy+=v;
        }
    }
    void add(ll l,ll r,ll L,ll R,ll v,ll x)
    {
        if(l>=L&&r<=R)//
        {
            t[x].s+=t[x].len*v;
            t[x].sum+=cala(x)*v;
            t[x].lsum+=calb(x)*v;
            t[x].rsum+=calb(x)*v;
            t[x].lzy+=v;
            return;
        }
        pushdown(x);
        ll mid=((l+r)>>1);
        if(R<=mid)add(l,mid,L,R,v,x<<1);
        else if(L>mid)add(mid+1,r,L,R,v,x<<1|1);
        else
        {
            add(l,mid,L,R,v,x<<1);
            add(mid+1,r,L,R,v,x<<1|1);
        }
        pushup(x);
    }
    ll qs(ll l,ll r,ll L,ll R,ll x)
    {
        if(l>=L&&r<=R)return t[x].s;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(R<=mid)return qs(l,mid,L,R,x<<1);
        if(L>mid)return qs(mid+1,r,L,R,x<<1|1);
        return qs(l,mid,L,R,x<<1)+qs(mid+1,r,L,R,x<<1|1);
    }
    ll qls(ll l,ll r,ll L,ll R,ll x)
    {
        if(l>=L&&r<=R)return t[x].lsum;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(L>mid)return qls(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return qls(l,mid,L,R,x<<1);
        return qls(mid+1,r,L,R,x<<1|1)+qls(l,mid,L,R,x<<1)+qs(l,mid,L,R,x<<1)*(R-mid);
    }
    ll qrs(ll l,ll r,ll L,ll R,ll x)
    {
        if(l>=L&&r<=R)return t[x].rsum;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(L>mid)return qrs(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return qrs(l,mid,L,R,x<<1);
        return qrs(mid+1,r,L,R,x<<1|1)+qrs(l,mid,L,R,x<<1)+qs(mid+1,r,L,R,x<<1|1)*(mid-L+1);
    }
    ll query(ll l,ll r,ll L,ll R,ll x)
    {
    //    cout<<l<<" "<<r<<endl;
    //    printf("*%lld %lld
    ",L,R);
        if(l>=L&&r<=R)return t[x].sum;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(L>mid)return query(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return query(l,mid,L,R,x<<1);
        return query(mid+1,r,L,R,x<<1|1)+query(l,mid,L,R,x<<1)
            +qrs(l,mid,L,R,x<<1)*(R-mid)+qls(mid+1,r,L,R,x<<1|1)*(mid-L+1);
    }
    int main()
    {
        scanf("%lld%lld",&n,&m);
        n--;//
        build(1,n,1);
        for(ll i=1,l,r,v;i<=m;i++)
        {
            char dc;
            cin>>dc;
            scanf("%lld%lld",&l,&r);//车站而非间距 
            r--;
            if(dc=='C')
            {
                scanf("%lld",&v);
                add(1,n,l,r,v,1);
            }
            if(dc=='Q')
            {
                ll k=query(1,n,l,r,1);
                yf(k,r-l+1);
            }
        }
        return 0;
    }

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll const maxn=5e5+5;
    ll n,m;
    struct N{
        ll sum,lsum,rsum,len,lzy;
        ll s;//!
    }t[maxn];
    ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
    void yf(ll x,ll nn)//nn
    {
        ll y=(1+nn)*nn/2;
        ll a=x,b=y;
        if(a<b)swap(a,b);
        ll c=gcd(a,b);
        printf("%lld/%lld
    ",x/c,y/c);
    }
    ll cala(ll x)//add
    {
        ll nn=t[x].len;
        ll n=nn-1;
        return (1+nn)*nn/2*nn-(1+n)*n/2-n*(n+1)*(n*2+1)/6;//
    }
    ll calb(ll x)//lsum,rsum add
    {
        ll nn=t[x].len;
        return (1+nn)*nn/2;
    }
    void pushup(ll x)
    {
        ll ls=(x<<1),rs=(x<<1|1);
        t[x].len=t[x<<1].len+t[x<<1|1].len;
        t[x].s=t[x<<1].s+t[x<<1|1].s;
        t[x].sum=t[ls].sum+t[rs].sum+t[ls].rsum*t[rs].len+t[rs].lsum*t[ls].len;
        t[x].lsum=t[ls].lsum+t[rs].lsum+t[ls].s*t[rs].len;//s
        t[x].rsum=t[ls].rsum+t[rs].rsum+t[rs].s*t[ls].len;
    }
    void build(ll l,ll r,ll x)
    {
    //    t[x].len=r-l+1;
        if(l==r)
        {
            t[x].len=1;
            return;
        }
        ll mid=((l+r)>>1);
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
        pushup(x);
    }
    void pushdown(ll x)
    {
        if(t[x].lzy)
        {
            ll v=t[x].lzy;t[x].lzy=0;
            ll ls=(x<<1),rs=(x<<1|1);
            t[ls].s+=t[ls].len*v;
            t[ls].sum+=cala(ls)*v;
            t[ls].lsum+=calb(ls)*v;
            t[ls].rsum+=calb(ls)*v;
            t[ls].lzy+=v;
            t[rs].s+=t[rs].len*v;
            t[rs].sum+=cala(rs)*v;
            t[rs].lsum+=calb(rs)*v;
            t[rs].rsum+=calb(rs)*v;
            t[rs].lzy+=v;
        }
    }
    void add(ll l,ll r,ll L,ll R,ll v,ll x)
    {
        if(l>=L&&r<=R)//
        {
            t[x].s+=t[x].len*v;
            t[x].sum+=cala(x)*v;
            t[x].lsum+=calb(x)*v;
            t[x].rsum+=calb(x)*v;
            t[x].lzy+=v;
            return;
        }
        pushdown(x);
        ll mid=((l+r)>>1);
        if(R<=mid)add(l,mid,L,R,v,x<<1);
        else if(L>mid)add(mid+1,r,L,R,v,x<<1|1);
        else
        {
            add(l,mid,L,mid,v,x<<1);
            add(mid+1,r,mid+1,R,v,x<<1|1);
        }
        pushup(x);
    }
    ll qs(ll l,ll r,ll L,ll R,ll x)
    {
        if(l==L&&r==R)return t[x].s;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(R<=mid)return qs(l,mid,L,R,x<<1);
        if(L>mid)return qs(mid+1,r,L,R,x<<1|1);
        return qs(l,mid,L,mid,x<<1)+qs(mid+1,r,mid+1,R,x<<1|1);
    }
    ll qls(ll l,ll r,ll L,ll R,ll x)
    {
        if(l==L&&r==R)return t[x].lsum;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(L>mid)return qls(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return qls(l,mid,L,R,x<<1);
        return qls(mid+1,r,mid+1,R,x<<1|1)+qls(l,mid,L,mid,x<<1)
            +qs(l,mid,L,mid,x<<1)*(R-mid);
    }
    ll qrs(ll l,ll r,ll L,ll R,ll x)
    {
        if(l==L&&r==R)return t[x].rsum;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(L>mid)return qrs(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return qrs(l,mid,L,R,x<<1);
        return qrs(mid+1,r,mid+1,R,x<<1|1)+qrs(l,mid,L,mid,x<<1)
            +qs(mid+1,r,mid+1,R,x<<1|1)*(mid-L+1);
    }
    ll query(ll l,ll r,ll L,ll R,ll x)//注意L,R要纳入计算 
    {
    //    cout<<l<<" "<<r<<endl;
    //    printf("*%lld %lld
    ",L,R);
        if(l==L&&r==R)return t[x].sum;//
        pushdown(x);
        ll mid=((l+r)>>1);
        if(L>mid)return query(mid+1,r,L,R,x<<1|1);
        if(R<=mid)return query(l,mid,L,R,x<<1);
        return query(mid+1,r,mid+1,R,x<<1|1)+query(l,mid,L,mid,x<<1)
            +qrs(l,mid,L,mid,x<<1)*(R-mid)+qls(mid+1,r,mid+1,R,x<<1|1)*(mid-L+1);//!
    }
    int main()
    {
        scanf("%lld%lld",&n,&m);
        n--;//
        build(1,n,1);
        for(ll i=1,l,r,v;i<=m;i++)
        {
            char dc;
            cin>>dc;
            scanf("%lld%lld",&l,&r);//车站而非间距 
            r--;
            if(dc=='C')
            {
                scanf("%lld",&v);
                add(1,n,l,r,v,1);
            }
            if(dc=='Q')
            {
                ll k=query(1,n,l,r,1);
                yf(k,r-l+1);
            }
        }
        return 0;
    }
  • 相关阅读:
    jquery源码解读 (摘自jQuery源码分析系列图书(pdf)) 持续更新
    jquery源码学习
    判断浏览器是ie9座特殊处理
    js继承
    引入flash
    IE7下面踩得坑
    导航栏对应相应的模块,可点击索引和滚动索引到需要到达的位置
    java的eclipse的使用
    弹窗在大屏上居中对齐,在小屏上被挡住解决方案
    左侧菜单栏,有对个li对应一个content
  • 原文地址:https://www.cnblogs.com/Zinn/p/9114220.html
Copyright © 2020-2023  润新知