BZOJ2752 || 洛谷P2221 [HAOI2012]高速公路【期望+线段树】

Time Limit: 20 Sec
Memory Limit: 128 MB

Description

Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。
Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。
政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。
无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的l,r(l

Input

第一行2个正整数N,M,表示有N个收费站,M次调整或询问
接下来M行,每行将出现以下两种形式中的一种
C l r v 表示将第l个收费站到第r个收费站之间的所有道路的通行费全部增加v
Q l r 表示对于给定的l,r,要求回答小A的问题
所有C与Q操作中保证1<=l

Output

对于每次询问操作回答一行,输出一个既约分数
若答案为整数a,输出a/1

HINT

所有C操作中的v的绝对值不超过10000
在任何时刻任意道路的费用均为不超过10000的非负整数
所有测试点的详细情况如下表所示
Test N M
1 =10 =10
2 =100 =100
3 =1000 =1000
4 =10000 =10000
5 =50000 =50000
6 =60000 =60000
7 =70000 =70000
8 =80000 =80000
9 =90000 =90000
10 =100000 =100000


题目分析

根据题意列表达式
a n s = ∑ i = L R ∑ j = L R d i s ( i , j ) C R − L + 1 2 ans=\frac{\sum_{i=L}^R\sum_{j=L}^Rdis(i,j)}{C_{R-L+1}^{2}} ans=CRL+12i=LRj=LRdis(i,j)

想办法维护上面那一坨,设 v i v_i vi为第 i i i条边的权值,重新表达分子
∑ i = L R − 1 w i ( i − L + 1 ) ( R − i + 1 ) \sum_{i=L}^{R-1}w_i(i-L+1)(R-i+1) i=LR1wi(iL+1)(Ri+1)
即对每条边计算有多少对点的路径经过它

展开后得
( R + 1 ) ( 1 − L ) ∑ w i + ( L + R ) ∑ i ∗ w i − ∑ i 2 w i (R+1)(1-L)\sum w_i+(L+R)\sum i*w_i-\sum i^2w_i (R+1)(1L)wi+(L+R)iwii2wi
用线段树分别维护三个和式即可

∑ i = 1 n i 2 = ( 2 n + 1 ) ∗ n ∗ ( n + 1 ) 6 \sum_{i=1}^ni^2=\frac{(2n+1)*n*(n+1)}{6} i=1ni2=6(2n+1)n(n+1)
注意边化点在后计算上的细节

#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=100010;
int n,m;
lt sum[3][maxn<<2],add[maxn<<2];
char ss[5];

lt gcd(lt a,lt b){ return b==0?a:gcd(b,a%b);}
lt calc(lt x){ return 1ll*(x*2+1)*x*(x+1)/6;}

void pushup(int p)
{
    int lc=p<<1,rc=p<<1|1;
    for(int i=0;i<3;++i) 
    sum[i][p]=sum[i][p<<1]+sum[i][p<<1|1];
}

void modify(lt s,lt t,lt p,lt v)
{
    sum[0][p]+=v*(t-s+1);
    sum[1][p]+=v*(s+t)*(t-s+1)/2;
    sum[2][p]+=v*(calc(t)-calc(s-1));
    add[p]+=v;
}

void pushd(lt s,lt t,lt mid,int p)
{
    if(!add[p]) return;
    modify(s,mid,p<<1,add[p]);
    modify(mid+1,t,p<<1|1,add[p]);
    add[p]=0;
}

void update(int ll,int rr,lt s,lt t,int p,lt v)
{
    if(ll<=s&&t<=rr){ modify(s,t,p,v); return;}
    int mid=s+t>>1;
    pushd(s,t,mid,p);
    if(ll<=mid) update(ll,rr,s,mid,p<<1,v);
    if(rr>mid) update(ll,rr,mid+1,t,p<<1|1,v);
    pushup(p);
}

lt query(int ll,int rr,lt s,lt t,int p,int d)
{
    if(ll<=s&&t<=rr) return sum[d][p];
    int mid=s+t>>1; lt res=0;
    pushd(s,t,mid,p);
    if(ll<=mid) res+=query(ll,rr,s,mid,p<<1,d);
    if(rr>mid) res+=query(ll,rr,mid+1,t,p<<1|1,d);
    return res;
}

lt C(lt a,lt b)
{
    lt res=1;
    for(lt i=1;i<=b;++i)
    res=res*(a-b+i)/i;
    return res;
} 

void solve(lt L,lt R)
{
    lt res=0;
    res+=(R+1)*(1ll-L)*query(L,R,1,n,1,0);
    res+=(L+R)*query(L,R,1,n,1,1);
    res-=query(L,R,1,n,1,2);
    
    //lt c=C(R-L+2,2);
    lt c=(R-L+2)*(R-L+1)/2;
    lt g=gcd(res,c);
    printf("%lld/%lld\n",res/g,c/g);
}

int main()
{
    n=read();m=read();
    while(m--)
    {
        scanf("%s",&ss);
        int ll=read(),rr=read()-1;
        if(ss[0]=='C') update(ll,rr,1,n,1,read());
        else if(ss[0]=='Q') solve(ll,rr);
    }
    return 0;
}

你可能感兴趣的:(线段树)