[HAOI2012] 高速公路

2023-05-16

这道题有一种解法是维护区间和,区间和 × i \times i ×i,区间和 × i 2 \times i^2 ×i2,但是这就需要很多的数学推导,这里有一种不同的方法,几乎不需要任何数学推导

你只需要会

  • 等差数列求和公式
  • 平方和公式
  • 数学期望的定义
  • 线段树

因为这道题的信息在边上,所以我们可以考虑让线段树存 ( l , r ) (l,r) (l,r)之间的边的信息

首先确定我们需要维护什么内容,因为这道题是选择点是等概率的,所以我们可以先求出所有方案的综合,再除以 C s i z 2 C_{siz}^2 Csiz2就可以了
所以显然我们需要维护一个所有方案的费用总和 a n s ans ans
但是只维护这一个显然是不行的,考虑我们合并两棵子树的信息的时候
在这里插入图片描述
我们的答案应该包括两个端点都在左边的情况,也就是 a n s [ l s o n ] ans[lson] ans[lson] a n s [ r s o n ] ans[rson] ans[rson]
但是这是不够的,因为两个端点有可能在 m i d mid mid的两边,也就是一个在左儿子的区间,一个在右儿子的区间
那么这种情况的答案是什么呢?
我们考虑让每个点走到 m i d mid mid,也就是对于一条边 ( x , y ) (x,y) (x,y),我们把它拆成 ( x , m i d ) (x,mid) (x,mid) ( m i d , y ) (mid,y) (mid,y)两段

对于左半部分的每一个点,他都需要和右半部分的每一个点连一条边,也就是说每个点都要向 m i d mid mid s i z [ r s o n ] − 1 siz[rson]-1 siz[rson]1次,同理,右半部分的每个点都要向左半部分的每个点连一条边,也就是说右半部分每个点都要向 m i d mid mid s i z [ l s o n ] − 1 siz[lson]-1 siz[lson]1次,然后考虑快速的维护这一信息,我们需要再维护两(三)个量

  • t o l tol tol表示这个区间内所有的点走向左端点的费用之和
  • t o r tor tor表示这个区间内所有的点走向右端点的费用之和
  • s i z siz siz表示这个区间的大小,但是其实也可以 r − l + 1 r-l+1 rl+1得到,但是为了方便记一下也行

那么这个时候我们的 a n s ans ans就可以快速更新了,在合并两棵子树的时候:

a n s [ i ] = a n s [ l s o n ] + a n s [ r s o n ] + t o r [ l s o n ] × ( s i z [ r s o n ] − 1 ) + t o l [ r s o n ] × ( s i z [ l s o n ] − 1 ) ans[i]=ans[lson]+ans[rson]+tor[lson]\times (siz[rson]-1)+tol[rson]\times (siz[lson]-1) ans[i]=ans[lson]+ans[rson]+tor[lson]×(siz[rson]1)+tol[rson]×(siz[lson]1)
注意这里 − 1 -1 1的原因是我们不需要走到 m i d mid mid,但是因为我们线段树存的是区间,所以 m i d mid mid这里左右子树是有交集的


那么现在又来了个问题,我们怎么更新 t o l tol tol t o r tor tor呢?
其实很简单, t o l [ i ] tol[i] tol[i]显然应该包含 t o l [ l s o n ] tol[lson] tol[lson],同时我们又需要让右子树中的每一个节点都再走一个左子树的费用,所以我们还需要维护一个

  • s u m sum sum表示该点的全部距离和

那么
t o l [ i ] = t o l [ l s o n ] + t o l [ r s o n ] + s u m [ l s o n ] × ( s i z [ r s o n ] − 1 ) tol[i]=tol[lson]+tol[rson]+sum[lson]\times (siz[rson]-1) tol[i]=tol[lson]+tol[rson]+sum[lson]×(siz[rson]1)
t o r tor tor的维护方法和 t o l tol tol差不多, s u m sum sum维护也挺简单,不说了

那么这样的话我们把合并子树的问题解决掉了,接下来还有一个问题就是对于区间修改怎么做


还是一个一个来说吧,首先是 a n s ans ans
显然, a n s ans ans应该加上的值是 v × v\times v×区间中所有边的长度和
比如对于这一个区间,我们分别考虑长度不同的边分别有几条
在这里插入图片描述
手画过丑勿喷
我们会发现对于一个长度为 s i z siz siz的区间,他拥有一条长度为 s i z − 1 siz-1 siz1的边,两条 s i z − 2 siz-2 siz2的边… s i z siz siz s i z − s i z = 0 siz-siz=0 sizsiz=0的边,转化成数学式子就是 ∑ i = 1 s i z i × ( s i z − i ) \sum_{i=1}^{siz} i\times(siz-i) i=1sizi×(sizi),然后我们拆开这个式子,得到 ∑ i = 1 s i z ( i × s i z + i 2 ) \sum_{i=1}^{siz} (i\times siz+i^2) i=1siz(i×siz+i2),也就是 s i z × ∑ i = 1 s i z i + ∑ i = 1 s i z s i z 2 siz\times \sum_{i=1}^{siz} i+\sum_{i=1}^{siz}siz^2 siz×i=1sizi+i=1sizsiz2,然后根据等差数列求和公式和前 n n n项平方和公式,我们可以得到对于一个长度为 s i z siz siz的区间,当每条边的费用增加 v v v之后,答案应该增加
v × ( s i z × s i z ( s i z − 1 ) 2 − s i z ( s i z + 1 ) ( 2 × s i z + 1 ) 6 ) v\times(siz\times \frac{siz(siz-1)}{2}-\frac{siz(siz+1)(2\times siz+1)}{6}) v×(siz×2siz(siz1)6siz(siz+1)(2×siz+1))
其实可以进一步分解,但是


接下来是 t o l tol tol t o r tor tor
这个其实很简单,加上 v × ∑ i = 1 s i z − 1 i v\times \sum_{i=1}^{siz-1} i v×i=1siz1i就可以
注意这里为什么只加到 s i z − 1 siz-1 siz1呢?因为左端点是不用走到左端点的
t o r tor tor同理
s u m sum sum不讲


然后就没了,写得时候注意一下细节就可以了
感觉这种方法比维护 i 2 i^2 i2什么的方法要好想一点,代码实现也不难,而且不用去找 h a c k hack hack数据,毕竟这个样例强度海星

下面是代码啦:

#include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=1e5+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,m;

struct node{
    int l,r;
    ll ans,tol,tor,siz,sum;
    ll tag;
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

ll gcd(ll a,ll b){
    if(!b)return a;
    return gcd(b,a%b);
}

node merge(node l,node r){
    node res;
    res.l=l.l,res.r=r.r;
    res.ans=l.ans+r.ans+l.tor*(r.siz-1)+r.tol*(l.siz-1);
    res.tol=l.tol+r.tol+l.sum*(r.siz-1);
    res.tor=l.tor+r.tor+r.sum*(l.siz-1);
    res.siz=res.r-res.l+1;
    res.sum=l.sum+r.sum;
    res.tag=0;
    return res;
}

void renew(int u,ll k){
    ll siz=seg[u].siz;
    seg[u].ans+=k*(siz*siz*(siz+1)/2-siz*(siz+1)*(2*siz+1)/6);
    seg[u].tol+=k*(siz*(siz-1)/2);
    seg[u].tor+=k*(siz*(siz-1)/2);
    seg[u].sum+=k*(siz-1);
}

void pushdown(int u){
    renew(lc,seg[u].tag);
    renew(rc,seg[u].tag);
    seg[lc].tag+=seg[u].tag;
    seg[rc].tag+=seg[u].tag;
    seg[u].tag=0;
}

void build(int u,int l,int r){
    seg[u].l=l,seg[u].r=r;
    seg[u].siz=r-l+1;
    if(r==l+1)return;
    int mid=l+r>>1;
    build(lc,l,mid);
    build(rc,mid,r);
}

void update(int u,int l,int r,ll k){
    if(seg[u].l>=l&&seg[u].r<=r){
        renew(u,k);
        seg[u].tag+=k;
        return;
    }
    if(seg[u].tag)pushdown(u);
    int mid=seg[u].l+seg[u].r>>1;
    if(l<mid)update(lc,l,r,k);
    if(r>mid)update(rc,l,r,k);
    seg[u]=merge(seg[lc],seg[rc]);
}

node query(int u,int l,int r){
    if(seg[u].l>=l&&seg[u].r<=r)return seg[u];
    if(seg[u].tag)pushdown(u);
    int mid=seg[u].l+seg[u].r>>1;
    if(r<=mid)return query(lc,l,r);
    if(l>=mid)return query(rc,l,r);
    return merge(query(lc,l,r),query(rc,l,r));
}

int main()
{
    read(n),read(m);
    build(1,1,n);
    Rep(i,1,m){
        char opt[10];
        ll x,y,k;
        scanf("%s",opt);
        read(x),read(y);
        if(opt[0]=='C')read(k),update(1,x,y,k);
        else{
            ll fz=query(1,x,y).ans;
            ll fm=1ll*(y-x+1)*(y-x)/2;
            ll _g=gcd(fm,fz);
            printf("%lld/%lld\n",fz/_g,fm/_g);
        }
    }
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • Python--for循环

    Python for循环 在python中 xff0c for循环可以遍历任何序列 xff0c 比如列表 字符串 for循环的基本格式如下 xff1a span class token keyword for span 变量 span cl
  • [BJWC2010] 严格次小生成树(kruskal+树剖)

    这题果然是模板题 一堆做法 但是根本思想是一样的 都是先跑一遍最小生成树 xff0c 然后维护一下路径上最大值和小于最大值的最大值 主要的实现方法有三种 1 kruskal 43 倍增 43 lca 复杂度是 O m l o
  • KEIL5安装与使用。

    1 KEIL5安装与使用 1 1 KEIL5软件获取 nbsp nbsp nbsp Keil官网下载地址 https www keil com download product nbsp 1 2 KEIL5软件安装 双击安装包 amp x
  • vs code Java运行问题:Build failed, do you want to continue?

    因为vscode构建将编译项目中的所有java文件 xff0c 所以 xff0c 应该是你的其他java文件存在一些问题 xff0c 所以无法通过编译 xff0c 但是这个你选择的java文件可以通过编译 xff0c 所以如果你继续 xff
  • 字符串的复制,将一串字符串复制到另一串字符串中 c语言简单易懂

    题目叙述 xff1a 编写一个函数 strcpy xff0c 其功能为将字符串 src 拷贝到字符数组 target xff0c 函数原型声明为 xff1a void strcpy char target char src xff1b 在
  • 类做友元应用案例 c++ 简单易懂

    span class token macro property span class token directive keyword include span span class token string lt iostream gt s
  • 拷贝构造函数的三种常见的调用时机 c++简单易懂

    拷贝构造函数的调用时机 xff1a 1 用一个已经创好的对象来初始化一个新对象 2 用值传递来给形参传值的时候 3 以值传递的方式返回局部对象都是利用拷贝构造函数的形式 分别用三个测试案例来举例 span class token macro
  • linux操作系统之cp命令(复制文件或文件夹)和mv(移动文件或文件夹) 通俗易懂

    一 xff1a cp命令 1 cp命令使用格式 xff1a cp 原文件 目标文件 2 简化 xff1a 当使用cp命令复制一个文件的时候 xff0c 如果文件名不发生改变那么目标文件只需要指明目标文件的路径即可 xff0c 就不用指明文件
  • 全局变量和局部变量的理解及注意事项 超详细 简单易懂

    一全局变量和局部变量 xff08 1 xff09 全局变量和局部变量的含义 xff1a 在函数体内部定义的变量叫做局部变量 xff0c 在函数体外部定义的变量叫做全局变量 局部变脸只能在定义的那个函数体的内部进行使用 xff0c 而全局变量
  • 光速幂

    warning xff1a 如果你还没有学过快速幂 xff0c 请掉头先学快速幂因为快速幂的适用范围比这个东西更广 我们先回忆一下快速幂是怎么解决的 我们是利用二进制的性质将复杂度优化到单词询问 O log i n
  • 队列的线性存储结构 c语言 数据结构 简单易懂 超详细~~

    include lt stdio h gt include lt stdlib h gt typedef int Elemtype define maxsize 100 typedef struct queue 注意再用顺序结构来表示栈和队
  • 复杂网络入门详解 适用于初学者。超详细~~

    一复杂网络的特性 1 复杂网络的特性之 小世界特性 xff1a xff08 1 xff09 社交网络中任何一个成员和任何一个认识的人之间的间隔人数不会超过六个人 即通过小于六个人 xff0c 总能找到社交网络中任何一个成员 xff08 2
  • Ubuntu系统中/usr/share/applications/目录下都是.desktop文件没有快捷方式

    在虚拟机中运行Ubuntu系统不免要安装一些linux应用软件 xff0c 为了方便我们会在虚拟机的桌面添加相应软件的快捷方式 一般情况下 xff0c 软件的快捷方式会保存在 usr share applications 目录下 我们可能会
  • my.cnf 中方便使用的设置

    记录my cnf 中一些方便使用的设置 vi etc my cnf 1 通过 prompt 61 name可以自定义提示信息 xff0c 通过配置显示登入的主机地址 xff0c 登陆用户名 xff0c 当前时间 xff0c 当前数据库sch
  • ubuntu环境下安装Jenkins

    文章目录 ubuntu环境下安装Jenkins方法一 war包安装1 34 启动脚本设置5 创建配置文件6 运行Jenkins 方法二 apt安装 问题记录1 启动jenkins报错 Failed to start Jetty或Failed
  • 打印1-100之间所有素数

    代码 方法1 方法2 执行结果 求1 10之间非素数之和
  • 打印出所有水仙花数

    水仙花数是指一个三位数 xff0c 其各位数字立方和等于该数本身 例如153 61 43 43 一重循环方式实现 首先分别求出三位数 i 的百位数 a 十位数 b 和个位数 c 之后判断a的立方和加b的立方和和c的立方和是否等于该三位数 i
  • LT8618SX寄存器配置

    LT8618SX功能 RGB输入 支持24位RGB xff0c YUV和BT656 BT601 BT1120输入 支持SDR和DDR数据采样 可编程上升 下降边缘时钟输入 支持高达148 5MHz DDR或297MHz SDR时钟输入 支持
  • linux重定向串口打印到telnet ssh远程终端

    源码 xff1a log c span class token macro property span class token directive hash span span class token directive keyword i
  • [HAOI2012] 高速公路

    这道题有一种解法是维护区间和 xff0c 区间和 i times i i xff0c 区间和 i 2