「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\)范围内的,并且为正整数

所以就阔以啦

原文地址:https://www.cnblogs.com/lizbaka/p/10440545.html

时间: 2024-08-11 16:51:21

「Luogu2221」[HAOI2012]高速公路的相关文章

「IOI2018」Highway 高速公路收费

目录 「IOI2018」Highway 高速公路收费 题目描述: 实现细节: 输入格式: 输出格式: 样例: 数据范围与提示: 子任务: 题解: Code 「IOI2018」Highway 高速公路收费 题目描述: 在日本,城市是用一个高速公路网络连接起来的.这个网络包含 \(N\) 个城市和 \(M\) 条高速公路.每条高速公路都连接着两个不同的城市.不会有两条高速公路连接相同的两个城市.城市的编号是从 \(0\) 到 \(N-1\) ,高速公路的编号则是从 \(0\) 到 \(M-1\) .

从「集装箱」思考Docker风潮

从「集装箱」思考Docker风潮 -- Docker潮流下的赢家策略 By 高焕堂 (台灣Docker聯盟 主席) 2015/02/20 前言 在许多革命性转折里,经常出现集装箱的身影:它就像幸运草一般,总是带来许多幸福和财运.现在Docker风起云涌,再现集装箱身影,如果开放视野.大力支持它,持续发挥它的潜能和力量,则幸运草就会出现在我们身旁了. 由于Docker集装箱带来的商机,其最直接的受益者是软件管理者(或称维运者),例如软件测试工具业者.测试人员等.因此在今天,不论您是开发者或是维运者

AC日记——「HNOI2017」单旋 LiBreOJ 2018

#2018. 「HNOI2017」单旋 思路: set+线段树: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxtree maxn<<2 int val[maxtree],tag[maxtree],L[maxtree],R[maxtree],mid[maxtree]; int op[maxn],ki[maxn],bi[maxn],cnt,size,n,ch[maxn]

「随笔」基于当下的思考

马德,说好的技术blog,变成日记本了... 下午的时候莫名其妙的感到很颓废,因为自己的不够强大感到忧虑和危机感十足.现在每每行走在技术的道路上,常觉得如履薄冰,如芒在背. 上大学之前和现在的心态其实差别挺大的,视野的开阔远远不止局限于自己的脚下.不过,这里的「上大学之前」只是一个时间描述词,并不觉得大学是最适合学习的地方,我很失望. 世界上的人无论性别,区域,宗教,兴趣爱好,总可以在互联网上找到志趣相同的人,总是可以不断打破自己的常识与惯性思维.总是有在相同领域比自己更强的人,挺好的. 关于知

「Unity」与iOS、Android平台的整合:3、导出的Android-Studio工程

本文属于「Unity与iOS.Android平台的整合」系列文章之一,转载请注明出处. Unity默认导出的是Android-Eclipse工程,毕竟Eclipse for Android开发在近一两年才开始没落,用户量还是非常巨大的. 个人认为AndroidStudio非常好用,能轻易解决很多Eclipse解决不了或者很难解决的问题. 所以我将Unity导出的Andoid工程分为Eclipse和AndroidStudio两部分. 不过我之后的相关内容都会使用AndroidStudio,希望依然

大数据和「数据挖掘」是何关系?---来自知乎

知乎用户,互联网 244 人赞同 在我读数据挖掘方向研究生的时候:如果要描述数据量非常大,我们用Massive Data(海量数据)如果要描述数据非常多样,我们用Heterogeneous Data(异构数据)如果要描述数据既多样,又量大,我们用Massive Heterogeneous Data(海量异构数据)--如果要申请基金忽悠一笔钱,我们用Big Data(大数据) 编辑于 2014-02-2817 条评论感谢 收藏没有帮助举报作者保留权利 刘知远,NLPer 4 人赞同 我觉得 大数据

开放的智力8:实用「成功学」

可实现的「成功学」 现在我想为这里的年轻人介绍一种可实现的「成功学」.希望这个我自创的理论,可以改变很多人的一生. 当我们评价一个事情值不值得去做.应该花多少精力去做的时候,应该抛弃单一的视角,而是分两个不同的维度来看,一是该事件将给我带来的收益大小(认知.情感.物质.身体方面的收益皆可计入),即「收益值」:二是该收益随时间衰减的速度,我称为「收益半衰期」,半衰期长的事件,对我们的影响会持续得较久较长. 这两个维度正交以后就形成了一个四象限图.我们生活.学习和工作中的所有事情都可以放进这个图里面

Linux 小知识翻译 - 「syslog」

这次聊聊「syslog」. 上次聊了「日志」(lgo).这次说起syslog,一看到log(日志)就明白是怎么回事了.syslog是获取系统日志的工具. 很多UINIX系的OS都采用了这个程序,它承担了「获取系统全部的日志」这个维持系统正常运行的重要任务. syslog的本体是「syslogd」这个daemon(一般翻译成守护进程),常驻内存中获取日志. syslog的特点是可以通过配置文件「/etc/syslog.conf」,对「哪种应用程序?哪种重要度的信息?记录在哪个文件中?」等进行细致的

Linux 小知识翻译 - 「日志」(log)

这次聊聊「日志」. 「日志」主要指系统或者软件留下的「记录」.出自表示「航海日志」的「logbook」. 经常听说「出现问题的时候,或者程序没有安装自己预期的来运行的时候,请看看日志!」. 确实,记录了系统和软件详细运行情况的「日志」是信息的宝库,通过日志来解决问题的事例也非常多. 但事实上,「无论如何也不会看日志」的用户也有很多.理由很简单,日志的信息量非常大,全部用眼睛来看的话是非常吃力的. 而且,英语写的日志也会让英文不好的人敬而远之. 虽说「要养成用眼睛来看日志的习惯」,但实行起来却非常