【Luogu P2221】 [HAOI2012]高速公路

题目链接

题目描述

Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。

Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。

政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。

无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的l,r(l

题解

首先注意一点:权值是边的,一次操作是将l,r之间的边的权值增加,和点没有一点关系。(不然你连样例都玩不出来 QAQ)

然后是做法:
既然每种情况的概率是相同的,且有 C ( 2 n ) 种。
那么就只需要求出区间l到r的所有路径的权值和。
对区间操作,考虑线段树。
首先为了方便,把边的权值扔到他前面那一个点上,且由于道路可逆,我们先只靠虑从编号小的到编号大的路径,之后乘上2就好。
对于一段长为L的区间,他第一个点的权值被算了 L 1 次,第二点被算了 2 ( L 2 ) 次。
手玩发现有如下公式。(Sum为该区间种路径的权值和,v为权值)

S u m = i = 1 L i ( L i ) v [ i ]

我们用的是线段树,所以不用管这个怎么快速算出来,只需管如何合并子树信息与答案即可。
维护一个答案ans,考虑怎么合并。边界很好搞,不多说。
n o w l s r s
l s , L i v [ i ] r s , L i l s L 1 r s L 2 r s ( L 1 + i ) ( L 2 + L 1 i L 1 ) v [ i ]
( L 1 + i ) ( L 2 i ) v [ i ] , L 1 ( L 2 i ) v [ i ]
发现只要再维护一个 ( L 2 i ) v [ i ]
于是我们先看怎么维护 i v [ i ] S ,这个在合并 l s r s S [ n o w ] = S [ l s ] + S [ r s ] + L 1 s u m [ r s ] ( s u m )
再看 ( L 2 i ) v [ i ] S 2 :
S [ n o w ] + S 2 [ n o w ] = L s u m [ n o w ] s u m
让后还要注意区间长度是看询问的…QAQ

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
typedef long long ll;
const int N=2e5+10;
struct node{
    ll sum;ll S;
    ll la;ll ans;
    void clear(){sum=S=la=ans=0;}
}tr[N<<2];
#define ls (now<<1)
#define rs (now<<1|1)
ll SD1[N];
ll SD2[N];//在区间修改时预处理的数组
//SD1=1+2+3+....+n
//SD2=sigma(i*(L-i))
int n,m;
inline ll gcd(ll a,ll b){return (b==0)? a:gcd(b,a%b);}
inline void push(int now,int x,int len)
{
    tr[now].la+=x;tr[now].sum+=1ll*len*x;
    tr[now].ans+=SD2[len]*x;tr[now].S+=SD1[len]*x;//自己想想,很好理解
}
inline void push_down(int now,int l,int r)
{
    if(!tr[now].la) return ;
    register int mid=l+r>>1;
    push(ls,tr[now].la,mid-l+1);push(rs,tr[now].la,r-mid);
    tr[now].la=0;
}
inline void updata(int now,int llen,int rlen)
{
    tr[now].sum=tr[ls].sum+tr[rs].sum;
    tr[now].S=tr[ls].S+tr[rs].S+tr[rs].sum*llen;
    tr[now].ans=tr[ls].ans+tr[rs].ans+tr[ls].S*rlen+(1ll*rlen*tr[rs].sum-tr[rs].S)*llen;
}
inline void add(int now,int l,int r,int L,int R,int x)
{
    push_down(now,l,r);
    if(l>=L&&r<=R) {push(now,x,r-l+1);return;}
    register int mid=l+r>>1;
    if(mid>=L) add(ls,l,mid,L,R,x);
    if(mid<R) add(rs,mid+1,r,L,R,x);
    updata(now,mid-l+1,r-mid);
}
inline node query(int now,int l,int r,int L,int R)
{
    push_down(now,l,r);
    if(l>=L&&r<=R) return tr[now];
    register int mid=l+r>>1;
    if(mid>=R) return query(ls,l,mid,L,R);
    else if(mid<L) return query(rs,mid+1,r,L,R);
    node p1=query(ls,l,mid,L,mid);node p2=query(rs,mid+1,r,mid+1,R);
    register int llen=mid-L+1;register int rlen=R-mid;//!!区间长度注意是跟询问的有关的(不是根据当前区间确定)
    node res={0,0,0,0};
    res.sum=p1.sum+p2.sum;
    res.S=p1.S+p2.S+p2.sum*llen;
    res.ans=p1.ans+p2.ans+p1.S*rlen+(1ll*rlen*p2.sum-p2.S)*llen;
    return res;
}
int main()
{
    n=read();m=read();
    register ll base=1;SD1[1]=1;
    for(register int i=2;i<=n;i++){
        base+=i;SD1[i]=base;SD2[i]=SD2[i-1]+SD1[i-1];
    }
    char ch;
    for(register int i=1;i<=m;i++)
    {
        ch=getchar();register int l,r;
        while(ch!='C'&&ch!='Q') ch=getchar();
        if(ch=='Q'){
            l=read();r=read();
            register ll d=r-l+1;register ll SI=d*(d-1);
            register ll SUM=2*(query(1,1,n,l,r)).ans;
            ll g=gcd(SUM,SI);
            SUM/=g;SI/=g;
            printf("%lld",SUM);
            putchar('/');printf("%lld\n",SI);
        }
        else{
            l=read();r=read();r--;
            register int x=read();
            add(1,1,n,l,r,x);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/element_hero/article/details/79679235