「Luogu2221」[HAOI2012]高速公路

「Luogu2221」[HAOI2012]高速公路

problem

题目描述

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

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

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

无聊的小\(A\)同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的\(l,r(l<r)\),在第\(l\)个到第\(r\)个收费站里等概率随机取出两个不同的收费站\(a\)\(b\),那么从\(a\)行驶到\(b\)将期望花费多少费用呢?

输入输出格式

输入格式:

第一行\(2\)个正整数\(N,M\),表示有\(N\)个收费站,\(M\)次调整或询问

接下来M行,每行将出现以下两种形式中的一种

C l r v
表示将第\(l\)个收费站到第\(r\)个收费站之间的所有道路的通行费全部增加\(v\)

Q l r
表示对于给定的\(l,r\),要求回答小\(A\)的问题

所有\(C\)\(Q\)操作中保证\(1\le l < r\le N\)

输出格式:

对于每次询问操作回答一行,输出一个既约分数

若答案为整数\(a\),输出a/1

输入输出样例

输入样例#1:

4 5
C 1 4 2
C 1 2 -1
Q 1 2
Q 2 4
Q 1 4

输出样例#1:

1/1
8/3
17/6

说明

所有\(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

Solution

首先非常感谢@GoldenPotato先辈和我一起颓柿子

讲真

这个题除了最终计算答案稍微要用一下期望

跟期望基本没有关系(绝望)


首先,对于每次询问,在区间\([l,r]\)中,任选两个点显然有\(C_{r-l+1}^{2}\)种选法,每种选法的概率都是\(\frac{1}{C_{r-l+1}^{2}}\)

令所有选法的长度之和为\(sum\),那么答案即为
\[\frac{sum}{C_{r-l+1}^{2}}\]

好了上面就是跟期望有关的部分,现在来看\(sum\)如何维护

\(v_i\)表示收费站\(i,i+1\)之间的的收费

画图可知,对于每次询问\(l,r\),考虑每一段的贡献,有\(sum=\sum_{i=1}^{r-l}i*(r-l-i+1)*v_{l+i-1}\)

\(l=2,r=6\)为例,那么\(sum\)即为\(1*4*v_2+2*3*v_3+3*2*v_4+4*1*v_5\)

显然这玩意很难维护,我们来颓柿子

\[\sum_{i=1}^{r-l}i*(r-l-i+1)*v_{l+i-1}\]
\[=(r-l+1)\sum_{i=1}^{r-l}i*v_{l+i-1}-\sum_{i=1}^{r-l}i^2*v_{l+i-1}\]

然而柿子中的各项依然比较难维护,因为每次\(l\)都是不同的,我们来继续变形

\[\sum_{i=1}^{r-l}i*v_{l+i-1}=\sum_{i=1}^{r-l}(l+i-1)*v_{l+i-1}-(l-1)\sum_{i=1}^{r-l}v_{l+i-1}\]

这样我们只需要维护\(i*v_i\)\(v_i\)的区间和即可求出\(\sum_{i=1}^{r-l}i*v_{l+i-1}\)

这不禁令我们想到我们是否可以通过维护\(i^2*v_i\)的区间和来处理剩下的一项

首先经过变形可得\((l+i-1)^2=(l-1)^2+(2l-2)i+i^2\)

于是有

\[\sum_{i=1}^{r-l}i^2*v_{l+i-1}=\sum_{i=1}^{r-l}(l+i-1)^2*v_{l+i-1}-(l-1)^2\sum_{i=1}^{r-l}v_{l+i-1}-(2l-2)\sum_{i=1}^{r-l}i*v_{l+i-1}\]

这样我们就又可以通过维护\(i^2*v_i\)的区间和求出\(\sum_{i=1}^{r-l}i^2*v_{l+i-1}\)推死我了我去

区间修改及区间求和显然可以通过线段树来维护

然而这几个量维护起来也挺恶心的

所以这个题其实是披着期望的外皮,实际上是颓柿子和数据结构的题

实际实现中要注意分清楚的编号

Code

非常丑,慎看

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 100005
#define maxm 100005
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

template <typename T> void read(T &t)
{
    t=0;ull f=0;char c=getchar();
    while(!isdigit(c)){f|=c=='-';c=getchar();}
    while(isdigit(c)){t=t*10+c-'0';c=getchar();}
    if(f)t=-t;
}

ull n,m;

ull gcd(ull a,ull b)
{
    return b?gcd(b,a%b):a;
}

inline ull IS(ull l,ull r){n}数列区间求和
{
    --l;
    return (r*(r+1)/2)-(l*(l+1)/2);
}

inline ull QS(ull l,ull r)//{n^2}数列区间求和
{
    --l;
    return (r*(r+1)*(2*r+1)/6)-(l*(l+1)*(2*l+1)/6);
}

#define lson (num<<1)
#define rson ((num<<1)|1)
struct sgt
{
    struct node
    {
        ull l,r;
        ull nons,is,qs,lazy;//vi,i*vi,i^2*vi
    }t[maxn<<2];
    void Build(ull l,ull r,ull num=1)
    {
        t[num].l=l;
        t[num].r=r;
        if(l==r)return;
        ull mid=(l+r)>>1;
        Build(l,mid,lson);
        Build(mid+1,r,rson);
    }
    void maintain(ull num)
    {
        t[num].nons=t[lson].nons+t[rson].nons;
        t[num].is=t[lson].is+t[rson].is;
        t[num].qs=t[lson].qs+t[rson].qs;
    }
    void pushdown(ull num)
    {
        ull llen=t[lson].r-t[lson].l+1,rlen=t[rson].r-t[rson].l+1;
        t[lson].lazy+=t[num].lazy,t[rson].lazy+=t[num].lazy;
        t[lson].nons+=llen*t[num].lazy;
        t[rson].nons+=rlen*t[num].lazy;
        t[lson].is+=t[num].lazy*IS(t[lson].l,t[lson].r);
        t[rson].is+=t[num].lazy*IS(t[rson].l,t[rson].r);
        t[lson].qs+=t[num].lazy*QS(t[lson].l,t[lson].r);
        t[rson].qs+=t[num].lazy*QS(t[rson].l,t[rson].r);
        t[num].lazy=0;
    }
    void ADD(ull l,ull r,ull x,ull num=1)
    {
        if(t[num].l>r || t[num].r<l)return;
        if(l<=t[num].l && r>=t[num].r)
        {
            t[num].lazy+=x;
            ull len=t[num].r-t[num].l+1;
            t[num].nons+=len*x;
            t[num].is+=IS(t[num].l,t[num].r)*x;
            t[num].qs+=QS(t[num].l,t[num].r)*x;//更新答案部分,至于为什么是这样可以自己推一下
            return;
        }
        pushdown(num);
        ADD(l,r,x,lson),ADD(l,r,x,rson);
        maintain(num);
    }
    ull nonsQuery(ull l,ull r,ull num=1)
    {
        if(t[num].l>r || t[num].r<l)return 0;
        if(l<=t[num].l && r>=t[num].r)
            return t[num].nons;
        pushdown(num);
        return nonsQuery(l,r,lson)+nonsQuery(l,r,rson);
    }
    ull isQuery(ull l,ull r,ull num=1)
    {
        if(t[num].l>r || t[num].r<l)return 0;
        if(l<=t[num].l && r>=t[num].r)
            return t[num].is;
        pushdown(num);
        return isQuery(l,r,lson)+isQuery(l,r,rson);
    }
    ull qsQuery(ull l,ull r,ull num=1)
    {
        if(t[num].l>r || t[num].r<l)return 0;
        if(l<=t[num].l && r>=t[num].r)
            return t[num].qs;
        pushdown(num);
        return qsQuery(l,r,lson)+qsQuery(l,r,rson);
    }
}t;

int main()
{
    read(n),read(m);
    t.Build(1,n-1);
    while(m--)
    {
        char op=getchar();
        while(op!='C' && op!='Q')op=getchar();
        if(op=='C')
        {
            ull l,r;ull x;
            read(l),read(r),read(x);
            t.ADD(l,r-1,x);
        }
        else
        {
            ull l,r;
            read(l),read(r);
            ull a=t.isQuery(l,r-1)-(l-1)*t.nonsQuery(l,r-1);
            ull b=t.qsQuery(l,r-1)-(l-1)*(l-1)*t.nonsQuery(l,r-1)-(2*l-2)*a;
            ull fz=a*(r-l+1)-b,fm=(r-l+1)*(r-l)/2,g=gcd(fz,fm);
            fz/=g,fm/=g;
            printf("%llu/%llu\n",fz,fm);
        }
    }
    return 0;
}

一点思考

请忽略以下胡言乱语

我发现这样算的话用\(long\space long\)是会豹的(捂脸)

所以我干脆把所有变量都改成\(unsigned\space long\space long\)

至于为什么在负数存在的情况下是正确的

如果使用\(ull\),实际上就是在模\(2^{64}-1\)的意义下做运算,而最终答案是在\(ll\)范围内的,并且为正整数

所以就阔以啦

猜你喜欢

转载自www.cnblogs.com/lizbaka/p/10440545.html