P3372 【模板】线段树 1
题目描述
如题,已知一个数列 {ai},你需要进行下面两种操作:
- 将某区间每一个数加上 k。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数 ai,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:
1 x y k:将区间 [x,y] 内每个数加上 k。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 项操作。
操作分为两种:
- 指定一个区间 [a,b],然后改变编号在这个区间内的灯的状态(把开着的灯关上,关着的灯打开);
- 指定一个区间 [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;
}