BZOJ5291/洛谷P4458/LOJ#2512 [Bjoi2018]链上二次求和 线段树
原文链接http://www.cnblogs.com/zhouzhendong/p/9031130.html
题目传送门 - LOJ#2512
题目传送门 - 洛谷P4458
题目传送门 - BZOJ5291
推荐LOJ和洛谷,题面质量好,而且不卡常数。
BZOJ题面烂,而且要卡那么一点点常数。
题意
有一条长度为$n$的链$\forall 1≤i<n$,点$i$与点$i+1$之间有一条边的无向图),每个点有一个整数权值,第$i$个点的权值是$a_i$。现在有$m$个操作,每个操作如下:
操作 1(修改):给定链上两个节点$u$、$v$和一个整数$d$,表示将链上$u$到$v$唯一的简单路径上每个点权值都加上$d$。
操作 2(询问):给定两个正整数$L$、$R$,表示求链上所有节点个数大于等于$L$且小于等于$R$的简单路径节点权值和之和。由于答案很大,只用输出对质数$1000000007$取模的结果即可。
一条节点个数为$k$的简单路径节点权值和为这条上所有$k$个节点(包括端点)的权值之和,而本题中要求是对所有满足要求的简单路径,求这一权值和的和。
由于是无向图,路径也是无向的,即点$1$到点$2$的路径与点$2$到点$1$的路径是同一条,不要重复计算。
题解
记$S_x[i]=\sum_{j=1}^{i} x[j]$,则我们现在来推一下式子。
对于询问,我们要求的是:
$$\begin{align*}\sum_{i=L}^{R}\sum_{j=i}^{n}S_a[j]-S_a[j-i]&=\sum_{i=L}^{R}(\sum_{j=i}^{n}S_a[j]-\sum_{j=0}^{n-i}S_a[j])\\&=\sum_{i=L}^{R}(S_{S_a}[n]-S_{S_a}[i-1]-S_{S_a}[n-i])\\&=(R-L+1)S_{S_a}[n]-\sum_{i=L-1}^{R-1}S_{S_a}[i]-\sum_{n-R}^{n-L}S_{S_a}[i]\end{align*}$$
只需要维护$S_{S_a}$的前缀和就可以了。
考虑分类讨论区间加对$S_{S_a}$的贡献。
在$L$~$R$区间加$v$的贡献为:
记$len=R-L+1$,
对于$L\leq i\leq R$,$S_{S_a}[i]=S_{S_a}[i]+\cfrac{(i-L+1)(i-L+2)}{2}v$。
对于$i>R$,$S_{S_a}[i]=S_{S_a}[i]+\cfrac{len(len+1)}{2}v+len(i-R)v$。
注意一下,对于修改,$L$和$R$可能是$L>R$的,我这里默认了$L\leq R$。
于是这个区间加对于$S_{S_a}$的贡献可以转化成一个关于$i$的二次函数。
可以用线段树来维护。
在BZOJ上面,略微卡一点常数。建议在标记下传的时候,如果三个标记都为$0$就直接退出。
题解参考了:https://blog.csdn.net/cdsszjj/article/details/80286084
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005;
const LL mod=1e9+7;
int n,m,a[N];
LL cv1[N],cv2[N];
struct Seg{
LL v,v0,v1,v2;
LL s0,s1,s2;
}t[N<<2];
void build(int rt,int L,int R){
t[rt].v0=t[rt].v1=t[rt].v2=0;
t[rt].s0=R-L+1;
if (L>0){
t[rt].s1=(cv1[R]-cv1[L-1]+mod)%mod;
t[rt].s2=(cv2[R]-cv2[L-1]+mod)%mod;
}
else
t[rt].s1=cv1[R],t[rt].s2=cv2[R];
if (L==R){
t[rt].v=a[L];
return;
}
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
build(ls,L,mid);
build(rs,mid+1,R);
t[rt].v=(t[ls].v+t[rs].v)%mod;
}
void addrt(int rt,LL d0,LL d1,LL d2){
t[rt].v=(t[rt].v+d0*t[rt].s0+d1*t[rt].s1+d2*t[rt].s2)%mod;
t[rt].v0=t[rt].v0+d0;
t[rt].v1=t[rt].v1+d1;
t[rt].v2=t[rt].v2+d2;
if (t[rt].v0>=mod)t[rt].v0-=mod;
if (t[rt].v1>=mod)t[rt].v1-=mod;
if (t[rt].v2>=mod)t[rt].v2-=mod;
}
void pushdown(int rt){
if (!t[rt].v0&&!t[rt].v1&&!t[rt].v2)
return;
int ls=rt<<1,rs=ls|1;
addrt(ls,t[rt].v0,t[rt].v1,t[rt].v2);
addrt(rs,t[rt].v0,t[rt].v1,t[rt].v2);
t[rt].v0=t[rt].v1=t[rt].v2=0;
}
void update(int rt,int L,int R,int xL,int xR,LL d0,LL d1,LL d2){
if (xL>xR)
return;
if (xL<=L&&R<=xR){
addrt(rt,d0,d1,d2);
return;
}
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
pushdown(rt);
if (xR<=mid)
update(ls,L,mid,xL,xR,d0,d1,d2);
else if (xL>mid)
update(rs,mid+1,R,xL,xR,d0,d1,d2);
else {
update(ls,L,mid,xL,mid,d0,d1,d2);
update(rs,mid+1,R,mid+1,xR,d0,d1,d2);
}
t[rt].v=(t[ls].v+t[rs].v)%mod;
}
LL query(int rt,int L,int R,int xL,int xR){
if (xL>xR)
return 0;
if (xL<=L&&R<=xR)
return t[rt].v;
int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
pushdown(rt);
if (xR<=mid)
return query(ls,L,mid,xL,xR);
else if (xL>mid)
return query(rs,mid+1,R,xL,xR);
else
return (query(ls,L,mid,xL,xR)+query(rs,mid+1,R,xL,xR))%mod;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
cv1[i]=(cv1[i-1]+i)%mod;
cv2[i]=(cv2[i-1]+1LL*i*i)%mod;
}
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),a[i]=(a[i]+a[i-1])%mod;
for (int i=1;i<=n;i++)
a[i]=(a[i]+a[i-1])%mod;
build(1,1,n);
for (int i=1;i<=m;i++){
int opt,L,R;
LL v;
scanf("%d%d%d",&opt,&L,&R);
if (L>R)
swap(L,R);
if (opt==2){
L=max(L,1);
LL s1=query(1,1,n,n,n)*(R-L+1)%mod;
LL s2=query(1,1,n,max(L-1,1),R-1);
LL s3=query(1,1,n,max(n-R,1),n-L);
printf("%lld\n",(s1-s2-s3+mod+mod+mod)%mod);
}
else {
scanf("%lld",&v);
LL len=R-L+1,iv2=500000004,vv=v*iv2%mod;
update(1,1,n,L,R,vv*(L-1)%mod*(L-2)%mod,vv*(3-2*L+mod)%mod,vv);
update(1,1,n,R+1,n,((len*(len+1)/2%mod*v-len*v%mod*R)%mod+mod)%mod,v*len%mod,0);
}
}
return 0;
}
BZOJ5291/洛谷P4458/LOJ#2512 [Bjoi2018]链上二次求和 线段树的更多相关文章
- 洛谷P4458 /loj#2512.[BJOI2018]链上二次求和(线段树)
题面 传送门(loj) 传送门(洛谷) 题解 我果然是人傻常数大的典型啊-- 题解在这儿 //minamoto #include<bits/stdc++.h> #define R regi ...
- [BZOJ5291][BJOI2018]链上二次求和(线段树)
感觉自己做的麻烦了,但常数似乎不算差.(只是Luogu最慢的点不到2s本地要跑10+s) 感觉我的想法是最自然的,但不明白为什么网上似乎找不到这种做法.(不过当然所有的做法都是分类大讨论,而我的方法手 ...
- 【BZOJ5291】[BJOI2018]链上二次求和(线段树)
[BZOJ5291][BJOI2018]链上二次求和(线段树) 题面 BZOJ 洛谷 题解 考虑一次询问\([l,r]\)的答案.其中\(S\)表示前缀和 \(\displaystyle \sum_{ ...
- BZOJ5291 BJOI2018链上二次求和(线段树)
用线段树对每种长度的区间维护权值和. 考虑区间[l,r]+1对长度为k的区间的贡献,显然其为Σk-max(0,k-i)-max(0,k-(n-i+1)) (i=l~r). 大力展开讨论.首先变成Σk- ...
- bzoj 5291: [Bjoi2018]链上二次求和
Description 有一条长度为n的链(1≤i<n,点i与点i+1之间有一条边的无向图),每个点有一个整数权值,第i个点的权值是 a_i.现在有m个操作,每个操作如下: 操作1(修改):给定 ...
- 2018.01.04 bzoj5291: [Bjoi2018]链上二次求和(线段树)
传送门 线段树基础题. 题意:给出一个序列,要求支持区间加,查询序列中所有满足区间长度在[L,R][L,R][L,R]之间的区间的权值之和(区间的权值即区间内所有数的和). 想题555分钟,写题202 ...
- loj2512 [BJOI2018]链上二次求和
传送门 分析 咕咕咕 代码 #include<iostream> #include<cstdio> #include<cstring> #include<st ...
- 「BJOI2018」链上二次求和
「BJOI2018」链上二次求和 https://loj.ac/problem/2512 我说今天上午写博客吧.怕自己写一上午,就决定先写道题. 然后我就调了一上午线段树. 花了2h找到lazy标记没 ...
- 洛谷P4459/loj#2511 [BJOI2018]双人猜数游戏(博弈论)
题面 传送门(loj) 传送门(洛谷) 题解 所以博弈论的本质就是爆搜么-- 题解 //minamoto #include<bits/stdc++.h> #define R registe ...
随机推荐
- 51nod--1264 线段相交 (计算几何基础, 二维)
题目: 1264 线段相交 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出平面上两条线段的两个端点,判断这两条线段是否相交(有一个公共点或有部分重合认为 ...
- 纯css使用线性渐变实现滚动进度条(来自于微信前端早读课)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【python】内存相关
1. /proc/pid/status 可以查看进程相关的详细信息,当内存异常时可查看 参考:http://blog.csdn.net/beckdon/article/details/4849190 ...
- hdu1540 区间合并+询问某点的最大连续块
询问操作需要搞一下 今天被区间合并降智了 /* D a: 摧毁第a个点 Q a:询问a所在的点的块大小 R :修复最后被破坏的点 对于所有的点需要进行一次更新 更新比较容易,tag用来表示区间是否是完 ...
- Mysql 查看连接数,状态 最大并发数
show status like '%max_connections%'; ##mysql最大连接数set global max_connections=1000 ##重新设置show variabl ...
- gitlab原理
GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务. 其实,说直白点写,他就是个git服务器,和github差不多,只不过,这个gitlab可 ...
- mysql如何在一张表中插入一万条数据?(用存储过程解决)
写一个存储过程,里面写一个循环,就可以了.主键你现在不是自增的,所以写语句的时候,就Insert到3个字段中. DELIMITER $$ DROP PROCEDURE IF EXISTS `proc_ ...
- JavaScript数组去重的6个方法
方法一无需思考,我们可以得到 O(n^2) 复杂度的解法.定义一个变量数组 res 保存结果,遍历需要去重的数组,如果该元素已经存在在 res 中了,则说明是重复的元素,如果没有,则放入 res 中. ...
- axios简单使用
介绍 我在使用vue的时候使用到了axios,vue 1.0的版本作者推荐使用vue-resource,到了vue 2.0作者建议使用axios,此篇文章只是我在使用axios时候做的笔记,我遇到的一 ...
- 【CF666E】Forensic Examination
题解: 熟练掌握了后缀自动机后大部分题目应该都比较容易想 首先对t建立广义后缀自动机 然后我们可以用线段树合并处理出每个点每个串出现的次数,然后求出最大值 匹配的时候比较巧妙 我们离线处理 对于同一个 ...