P2221 [HAOI2012]高速公路

传送门

区间修改区间询问,考虑线段树维护

对于一个询问 $l,r$ ,如果我们能求出总每种情况的总花费,那么最终答案就是总花费除 $C^2_{r-l+1}$

为了方便维护把每条边看成点,那么线段树有 $n-1$ 个叶子节点

考虑线段树两个相邻区间 $lc,rc$ 合并成区间 $O$

显然先设 $sum$ 表示当前区间的总费用,那么 $sum[O]$ 肯定先加上左右区间内部的贡献

接下来只要考虑跨过中间的贡献

考虑每条边单独产生的贡献,显然是边长乘经过次数

考虑一个 $lc$ 的一条边 $x$ 会被跨中间的路径经过几次,设边 $x$ 在区间 $O$ 中左边的点数为 $t[x]$(此处的点为原序列的点,不是线段树的点)

那么对于左边的每个点,它都可以与 $rc$ 的每个点作为一段路径,设 $rc$ 的包含的总边数为 $sz[r]$,那么总点数为 $sz[r]+1$,但是因为 $rc$ 最左边的点和 $lc$ 最右边的点是同一个,所以计算贡献的点只有 $sz[rc]$ 而不是 $sz[rc]+1$,之后有些地方也要注意到这点

所以贡献为 $t[x]*dis[x]*sz[rc]$,发现对于 $lc$ 的每个边 $x$ 它都要乘 $sz[rc]$ ,所以只要考虑维护所有 $t[x]*dis[x]$ 的和,设这个东西为 $suml[ ]$,那么对 $sum[O]$的贡献就是 $suml[lc]*sz[rc]$

考虑合并时 $suml[O]$ 的变化,发现对于 $O$ 的左右子区间 $lc,rc$, $lc$ 对 $O$ 的贡献就是 $suml[lc]$,但是 $rc$ 对 $O$ 的贡献为 $suml[rc]$ 加上 $rc$ 的每条边新增加的贡献,其实就是 $rc$ 的所有 $t[ ]$都增加了 $sz[lc]$ 总贡献增加 $tot[rc]*sz[lc]$ ( $tot[x]$ 为区间 $x$ 的每条边边长之和)

然后我们就可以维护 $suml$ 进而维护 $lc$ 对 $sum[o]$ 的贡献了,然后考虑 $rc$ 对 $sum[O]$ 的贡献

同样维护 $sumr[rc]$ 表示区间 $rc$ 所有边分别乘上它们右边的点数的和,同 $suml$ 差不多的方法维护

总结一下就是如下代码 (为了方便写进了结构体):

inline dat operator + (const dat &tmp) const {
        if(!sz) return tmp;//先不用管这个
        dat res; res.clr();
        res.sum= sum + tmp.sum + suml*tmp.sz + tmp.sumr*sz;//左右区间内部的贡献加上跨左右区间的贡献
        res.suml= suml + tmp.suml + tmp.tot*sz;//维护suml
        res.sumr= tmp.sumr + sumr + tot*tmp.sz;//维护sumr
        res.tot=tot+tmp.tot; res.sz=sz+tmp.sz;//维护tot和sz
        return res;
    }

因为有区间修改,考虑标记 $add$ 对这些东西的影响:

  首先 $tot$ 显然加上 $sz*add$

  对于 $suml$,因为每条边都增加 $add$,而区间内 $t[x]$ 为等差数列 $1,2,3...sz[x]$,所以运用数列求和的方法求出总贡献增加量为:

  $add+2add+3add+...+sz*add=sz(sz+1)/2*add$

    $sumr$同理,

  对于 $sum$ 就比较复杂,仍然考虑每条边增加的贡献,那么还是可以写出式子:

  $(1*sz)add+(2*(sz-1))add+(3*(sz-2))add+...+(sz*1)add$

  然后经过一波丧心病狂的化简:

  $=  ( sz+2(sz-1)+3(sz-2)+...+sz*(sz-(sz-1)) ) *add$

  $=  ( sz+2*sz+3*sz+...+sz*sz - (2(2-1)+3(3-1)+4(4-1)+...+sz(sz-1)) ) *add$

  $=  ( (1+2+3+...+sz)sz - ( 2^2+3^2+4^2+...+sz^2 - (2+3+4+...+sz) ) ) *add$

  因为 $ sum _{i=1}^{n}i^2=n(n+1)(2n+1)/6$

  所以 $=  ( (1+2+3+...+sz)sz - ( sz(sz+1)(2sz+1)/6 -1 - (2+3+4+...+sz) ) ) *add$

  $=  ( (1+2+3+...+sz)sz - ( sz(sz+1)(2sz+1)/6 - (1+2+3+4+...+sz) ) ) *add$

  $=  ( sz^2*(sz+1)/2 - ( sz(sz+1)(2sz+1)/6 - sz(sz+1)/2 ) ) *add$

  $=  ( (sz(sz+1)/2)*(sz+1) - sz(sz+1)(2sz+1)/6 ) *add$

  $=  ( sz(sz+1)^2/2 - sz(sz+1)(2sz+1)/6 ) *add$

所以最后增加的就是这个东西..........

那么更新节点代码如下:

inline void upd()
{
    tot+=sz*add;
    suml+=sz*(sz+1)/2*add;
    sumr+=sz*(sz+1)/2*add;
    sum+=(sz*(sz+1)*(sz+1)/2-sz*(sz+1)*(sz*2+1)/6)*add;
    add=0;
}

其他的就简单了,记得开 $unsigned long long$!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef unsigned long long ull;
inline ull read()
{
    ull x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const ull N=2e5+7;
ull n,m;
struct dat{
    ull suml,sumr,sum,tot,sz,add;
    inline void clr() { suml=sumr=sum=tot=sz=add=0; }
    inline void upd()
    {
        tot+=sz*add;
        suml+=sz*(sz+1)/2*add;
        sumr+=sz*(sz+1)/2*add;
        sum+=(sz*(sz+1)*(sz+1)/2-sz*(sz+1)*(sz*2+1)/6)*add;
        add=0;
    }
    inline dat operator + (const dat &tmp) const {
        if(!sz) return tmp;//因为ans初始为空所以特判一下
        dat res; res.clr();
        res.sum= sum + tmp.sum + suml*tmp.sz + tmp.sumr*sz;
        res.suml= suml + tmp.suml + tmp.tot*sz;
        res.sumr= tmp.sumr + sumr + tot*tmp.sz;
        res.tot=tot+tmp.tot; res.sz=sz+tmp.sz;
        return res;
    }
}T[N<<2],ans;
inline void pushdown(ull o,ull l,ull r)
{
    if(!T[o].add) return;
    if(l!=r) T[o<<1].add+=T[o].add,T[o<<1|1].add+=T[o].add;
    T[o].upd();
}
ull ql,qr; ull v;

void build(ull o,ull l,ull r)
{
    T[o].sz=r-l+1; if(l==r) return;
    ull mid=l+r>>1;
    build(o<<1,l,mid); build(o<<1|1,mid+1,r);
}
void change(ull o,ull l,ull r)
{
    pushdown(o,l,r);
    if(qr<l||ql>r) return;
    if(l>=ql&&r<=qr) { T[o].add+=v; pushdown(o,l,r); return; }
    ull mid=l+r>>1;
    change(o<<1,l,mid); change(o<<1|1,mid+1,r);
    T[o]=T[o<<1]+T[o<<1|1];
}
void query(ull o,ull l,ull r)
{
    pushdown(o,l,r);
    if(qr<l||ql>r) return;
    if(l>=ql&&r<=qr) { ans=ans+T[o]; return; }
    ull mid=l+r>>1;
    query(o<<1,l,mid); query(o<<1|1,mid+1,r);
    T[o]=T[o<<1]+T[o<<1|1];
}
ull gcd(ull x,ull y) { return y ? gcd(y,x%y) : x; }
int main()
{
    n=read(),m=read();
    build(1,1,n-1);
    char s[7];
    while(m--)
    {
        scanf("%s",s); ql=read(),qr=read()-1;
        if(s[0]=='C') v=read(),change(1,1,n-1);
        else
        {
            ans.clr(); query(1,1,n-1);
            ull b=(qr-ql+2)*(qr-ql+1)/2,g=gcd(ans.sum,b); 
            //b不是(qr-ql+1)*(qr-ql)/2是因为之前qr有-1
            printf("%lld/%lld
",ans.sum/g,b/g);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/10623601.html