洛谷P2221 [HAOI2012]高速公路

题目:P2221 [HAOI2012]高速公路

思路:

这道题是在线段树专题讲的,转化的思路可能自己不太容易想到,记录一下。

难度集中在转化设问上面。问题可以简化成区间([l,r-1])的问题,实际上就是算所有子区间的区间和然后再除(C(len,2))
我们来观察这个区间和,发现直接做貌似不太好搞,于是想到了考虑每个数对区间答案的贡献,之后可以写出(O(n))修改、查询的暴力的式子:
(sum_{i=l}^r{s[i]*(i-l+1)*(r-i+1)*2})
继续观察,发现式子可以拆成(s[i])(i*s[i])(i^2*s[i])的区间和的常数倍,于是就可以用区间加减的线段树搞了。

已经讲的很清楚了。
式子可以化为:
(2*((l+r)sum_{i=l}^r{i*s[i]}+(r-l+1-l*r)*sum_{i=l}^r{s[i]}-sum_{i=l}^r{i^2*s[i]}))
然后分别维护三个值:(s[i])直接维护,(i*s[i])用等差数列求和,(i^2*s[i])(sum_{i=1}^n{i^2}=frac {n(n+1)(2n+1)} 6)
细节:边化点,维护序列长n-1;计算的时候想清楚r用不用减1;记得开longlong。
难点:最初的思路转化:把原问题转化为计算每条边的贡献。


Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+5;
ll n,m,ans1,ans2,ans3;
struct Tree{
    ll l,r,sum1,sum2,sum3,tag;
#define l(p) (node[p].l)
#define r(p) (node[p].r)
#define sum1(p) (node[p].sum1)
#define sum2(p) (node[p].sum2)
#define sum3(p) (node[p].sum3)
#define tag(p) (node[p].tag)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define len(p) (r(p)-l(p)+1)
#define mid ((l(p)+r(p))>>1)
}node[N<<2];
ll calc(ll n){
    return n*(n+1)*(2*n+1)/6;
}
void pup(ll p){
    sum1(p)=sum1(ls(p))+sum1(rs(p));
    sum2(p)=sum2(ls(p))+sum2(rs(p));
    sum3(p)=sum3(ls(p))+sum3(rs(p));
}
void update(ll p,ll val){
    tag(p)+=val;
    sum1(p)+=len(p)*val;
    sum2(p)+=val*(l(p)+r(p))*len(p)/2;
    sum3(p)+=(calc(r(p))-calc(l(p)-1))*val;
}
void pdown(ll p){
    if(tag(p)){
        update(ls(p),tag(p));
        update(rs(p),tag(p));
        tag(p)=0;
    }
}
void build(ll p,ll l,ll r){
    l(p)=l;r(p)=r;
    if(l==r) return;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
}
void change(ll p,ll L,ll R,ll val){
    if(l(p)>R||r(p)<L) return;
    if(L<=l(p)&&r(p)<=R) return update(p,val);
    pdown(p);
    change(ls(p),L,R,val);
    change(rs(p),L,R,val);
    pup(p);
}
void query(ll p,ll L,ll R){
    if(l(p)>R||r(p)<L) return;
    if(L<=l(p)&&r(p)<=R) return (void) (ans1+=sum1(p),ans2+=sum2(p),ans3+=sum3(p));
    pdown(p);
    query(ls(p),L,R);
    query(rs(p),L,R);
}
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
int main(){
    scanf("%lld%lld",&n,&m);
    build(1,1,n-1);
    char op[5]; ll l,r,val;
    for(ll i=1;i<=m;++i){
        scanf("%s",op);
        if(op[0]=='C') scanf("%lld%lld%lld",&l,&r,&val),change(1,l,r-1,val);
        else{
            scanf("%d%d",&l,&r);
            --r;
            ans1=ans2=ans3=0;
            query(1,l,r);
            ll res=(l+r)*ans2-ans3+(r-l-l*r+1)*ans1,div=(r-l+2)*(r-l+1)/2,d=gcd(res,div);
            res/=d,div/=d;
            printf("%lld/%lld
",res,div);
        }
    }
    return 0;
}