洛谷-【数据结构2-2】线段树1

P3372 【模板】线段树 1

题目描述

如题,已知一个数列 {ai​},你需要进行下面两种操作:

  1. 将某区间每一个数加上 k。
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n 个用空格分隔的整数 ai​,其中第 i 个数字表示数列第 i 项的初始值。

接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:

  1. 1 x y k:将区间 [x,y] 内每个数加上 k。
  2. 2 x y:输出区间 [x,y] 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

输入输出样例

输入 #1复制

复制代码
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

输出 #1复制

复制代码
11
8
20

说明/提示

对于 15% 的数据:n≤8,m≤10。

对于 35% 的数据:n≤103,m≤104。

对于 100% 的数据:1≤n,m≤105,ai​,k 为正数,且任意时刻数列的和不超过 2×1018。

【样例解释】

实现代码:

cpp 复制代码
#include<iostream>
#include<cstdio>
#define MAXN 1000001
#define ll long long
using namespace std;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline ll ls(ll x)
{
    return x<<1;
}
inline ll rs(ll x)
{
    return x<<1|1;
}
void scan()
{
    cin>>n>>m;
    for(ll i=1;i<=n;i++)
    scanf("%lld",&a[i]);
}
inline void push_up(ll p)
{
    ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r)
{
    tag[p]=0;
    if(l==r){ans[p]=a[l];return ;}
    ll mid=(l+r)>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);
} 
inline void f(ll p,ll l,ll r,ll k)
{
    tag[p]=tag[p]+k;
    ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r)
{
    ll mid=(l+r)>>1;
    f(ls(p),l,mid,tag[p]);
    f(rs(p),mid+1,r,tag[p]);
    tag[p]=0;
}
inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k)
{
    if(nl<=l&&r<=nr)
    {
        ans[p]+=k*(r-l+1);
        tag[p]+=k;
        return ;
    }
    push_down(p,l,r);
    ll mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
    if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
    push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p)
{
    ll res=0;
    if(q_x<=l&&r<=q_y)return ans[p];
    ll mid=(l+r)>>1;
    push_down(p,l,r);
    if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
    if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
    return res;
}
int main()
{
    ll a1,b,c,d,e,f;
    scan();
    build(1,1,n);
    while(m--)
    {
        scanf("%lld",&a1);
        switch(a1)
        {
            case 1:{
                scanf("%lld%lld%lld",&b,&c,&d);
                update(b,c,1,n,1,d);
                break;
            }
            case 2:{
                scanf("%lld%lld",&e,&f);
                printf("%lld\n",query(e,f,1,n,1));
                break;
            }
        }
    }
    return 0;
}

P3870 [TJOI2009] 开关

提交答案加入题单复制题目

题目描述

现有 n 盏灯排成一排,从左到右依次编号为:1,2,......,n。然后依次执行 m 项操作。

操作分为两种:

  1. 指定一个区间 [a,b],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
  2. 指定一个区间 [a,b],要求你输出这个区间内有多少盏灯是打开的。

灯在初始时都是关着的。

输入格式

第一行有两个整数 n 和 m,分别表示灯的数目和操作的数目。

接下来有 m 行,每行有三个整数,依次为:c、a、b。其中 c 表示操作的种类。

  • 当 c 的值为 0 时,表示是第一种操作。
  • 当 c 的值为 1 时,表示是第二种操作。

a 和 b 则分别表示了操作区间的左右边界。

输出格式

每当遇到第二种操作时,输出一行,包含一个整数,表示此时在查询的区间中打开的灯的数目。

输入输出样例

输入 #1复制

复制代码
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4

输出 #1复制

复制代码
1
2

说明/提示

数据规模与约定

对于全部的测试点,保证 2≤n≤105,1≤m≤105,1≤a,b≤n,c∈{0,1}。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000001;
int ans,n,m,a,b,c;
struct tree
{
	int l,r,sum,add;
}t[maxn];
inline void build(int u,int x,int y)
{
    t[u].l=x;
	t[u].r=y;
	if(x==y)
	{
		t[u].sum=0;
		return ;
	}
	int mid=(x+y)>>1;
	build(u*2,x,mid);build(u*2+1,mid+1,y);
}
inline void tag(int u)
{
	if(t[u].add==0)return;
	t[u*2].sum=t[u*2].r-t[u*2].l+1-t[u*2].sum;
	t[u*2+1].sum=t[u*2+1].r-t[u*2+1].l+1-t[u*2+1].sum;
	if(t[u*2].add==0)
	t[u*2].add=1;
	else t[u*2].add=0;
	if(t[u*2+1].add==0)
	t[u*2+1].add=1;
	else t[u*2+1].add=0;
	t[u].add=0;
}
inline void change(int u,int l,int r)
{
	if(l<=t[u].l&&t[u].r<=r)
	{
		t[u].sum=t[u].r-t[u].l+1-t[u].sum;
		if(t[u].add==0)
		t[u].add=1;
		else 
		t[u].add=0;
		return ;
	}
	tag(u);
	int mid=(t[u].l+t[u].r)>>1;
	if(a<=mid)change(u*2,l,r);
	if(b>mid)change(u*2+1,l,r);
	t[u].sum=t[u*2].sum+t[u*2+1].sum;
}
inline int ask(int u,int l,int r)
{
	if(l<=t[u].l&&r>=t[u].r)return t[u].sum;
	tag(u);
	int mid=(t[u].l+t[u].r)/2;
	int ans=0;
	if(a<=mid)ans+=ask(u*2,l,r);
	if(b>mid)ans+=ask(u*2+1,l,r);
	return ans;
}
int main()
{
	cin>>n>>m;
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		cin>>c>>a>>b;
		if(c==0)
		change(1,a,b);
		else
		cout<<ask(1,a,b)<<endl;
	}
	return 0;
}

P1438 无聊的数列

题目背景

无聊的 YYB 总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的 YYB 想出了一道无聊的题:无聊的数列。。。

题目描述

维护一个数列 ai​,支持两种操作:

  • 1 l r K D:给出一个长度等于 r−l+1 的等差数列,首项为 K,公差为 D,并将它对应加到 [l,r] 范围中的每一个数上。即:令 al​=al​+K,al+1​=al+1​+K+D...ar​=ar​+K+(r−l)×D。

  • 2 p:询问序列的第 p 个数的值 ap​。

输入格式

第一行两个整数 n,m 表示数列长度和操作个数。

第二行 n 个整数,第 i 个数表示 ai​。

接下来的 m 行,每行先输入一个整数 opt。

若 opt=1 则再输入四个整数 l r K D;

若 opt=2 则再输入一个整数 p。

输出格式

对于每个询问,一行一个整数表示答案。

输入输出样例

输入 #1复制

复制代码
5 2
1 2 3 4 5
1 2 4 1 2
2 3

输出 #1复制

复制代码
6

说明/提示

数据规模与约定

对于 100% 数据,0≤n,m≤105,−200≤ai​,K,D≤200,1≤l≤r≤n,1≤p≤n。

实现代码:

cpp 复制代码
#include<iostream>
using namespace std;
#define ll long long
ll data[100005];
struct point{
    ll sum;
    ll tag;
} a[400005];
inline int ls(int root){return root<<1;}
inline int rs(int root){return root<<1|1;}
inline void up(int root){a[root].sum=a[ls(root)].sum+a[rs(root)].sum;}
void build(int root,int l,int r){
    a[root].tag=0;int mid=(l+r)>>1;
    if(l==r){a[root].sum=data[l];return;}
    build(ls(root),l,mid);build(rs(root),mid+1,r);
    up(root);
}
inline void pd(int root,int l,int r){
    int mid=(l+r)>>1;
    a[ls(root)].tag+=a[root].tag;
    a[rs(root)].tag+=a[root].tag;
    a[ls(root)].sum+=a[root].tag*(mid-l+1);
    a[rs(root)].sum+=a[root].tag*(r-mid);
    a[root].tag=0;
}
void add(int root,int l,int r,int ql,int qr,ll x){
    if(ql<=l&&qr>=r){a[root].tag+=x;a[root].sum+=(r-l+1)*x;return;}
    int mid=(l+r)>>1;
    pd(root,l,r);
    if(ql<=mid)add(ls(root),l,mid,ql,qr,x);
    if(qr>mid) add(rs(root),mid+1,r,ql,qr,x);
    up(root);
    return;
}
ll query(int root,int l,int r,int ql,int qr){
    if(ql<=l&&qr>=r)
        return a[root].sum;
    int mid=(l+r)>>1,ret=0;
    pd(root,l,r);
    if(ql<=mid)ret+=query(ls(root),l,mid,ql,qr);
    if(qr>mid)ret+=query(rs(root),mid+1,r,ql,qr);
    return ret;
}
int main()
{
    int n,m,opt,l,r,k,d,t;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>data[i];
    for(int i=n-1;i>0;i--)
        data[i+1]=data[i+1]-data[i];
    build(1,1,n);
    for(int i=0;i<m;i++){
        cin>>opt;
        if(opt==1){
            cin>>l>>r>>k>>d;
            add(1,1,n,l,l,k);
            if(l+1<=r)add(1,1,n,l+1,r,d);
            if(r<n)add(1,1,n,r+1,r+1,-(k+d*(r-l)));
        }
        else{
            cin>>t;
            cout<<query(1,1,n,1,t)<<endl;
        }
        
    }
    return 0;
}
相关推荐
鸠摩智首席音效师1 小时前
如何在 Bash 中通过 Amazon SES 发送电子邮件 ?
开发语言·bash
ZC跨境爬虫1 小时前
跟着 MDN 学 HTML day_43:(DocumentFragment 接口详解)
前端·javascript·vue.js·ui·html·音视频
电科一班林耿超1 小时前
机器学习大师课 第 8 课:端到端项目实战 —— 泰坦尼克号生存预测
人工智能·算法·机器学习
~|Bernard|1 小时前
五,go语言的内存管理
开发语言·后端·golang
ComputerInBook1 小时前
数字图像处理(4版)——第 12 章——图像模式分类(上)(Rafael C.Gonzalez&Richard E. Woods)
图像处理·人工智能·算法·模式识别·图像模式分类
y = xⁿ1 小时前
20天速通LeetCodeday13:DFS深度优先搜素
算法·深度优先
七牛开发者1 小时前
开源项目观察|ds4:本地 Agent 推理,不只是把模型跑起来
人工智能·redis·算法·开源
Cx330❀1 小时前
从零实现一个 C++ 轻量级日志系统:原理与实践
大数据·linux·运维·服务器·开发语言·c++·搜索引擎
phltxy1 小时前
Redis 数据结构之 List 详细解析
数据结构·redis·list