243. 一个简单的整数问题2
题目链接
243. 一个简单的整数问题2
给定一个长度为 \(N\) 的数列 \(A\),以及 \(M\) 条指令,每条指令可能是以下两种之一:
-
C l r d
,表示把 \(A[l],A[l+1],…,A[r]\) 都加上 \(d\)。 -
Q l r
,表示询问数列中第 \(l \sim r\) 个数的和。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数 \(N,M\)。
第二行 \(N\) 个整数 \(A[i]\)。
接下来 \(M\) 行表示 \(M\) 条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
\(1 \le N,M \le 10^5\),
\(|d| \le 10000\),
\(|A[i]| \le 10^9\)
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
解题思路
线段树
线段树模板题,有打懒标记和标记永久化两种做法
-
懒标记:即每次操作需要将标记下传,即动态修改信息
-
标记永久化:有局限性,不能处理相互的影响的多个标记,维护整个区间,标记打到表示整个区间的节点上时就不再往下传,而是在查询记录结果时加上往下传的标记带来的贡献
-
时间复杂度:\(O(mlogn)\)
zkw线段树
相对于普通线段树而言,码量更小,常数更小,大多数情况下能够取代普通线段树,但是不能处理维护优先级的信息(如区间加和乘的混合运算),一般自底向上维护信息,原因在于zkw线段树本质上是一个满二叉树,找叶子节点很方便,判断 \(l\) 和 \(r\) 是否为兄弟节点也很方便:l^r=1
- 时间复杂度:\(O(mlogn)\)
树状数组
维护一个差分数组 \(b[i]\),\(\sum_{i=1}^x b[i]\) 即为 \(a[x]\) 变化的值,同理可得序列 \(a\) 的前缀和 \(a[1 \sim x]\). 整体增加的值就是:
上式可以改写为:
维护两个树状数组即可
- 时间复杂度:\(O(mlogn)\)
分块
将 \(1\sim n\) 的整个区间分为 \(\sqrt{n}\) 个小区间,每次修改或询问采用全局标记,局部朴素的思想,即将所有完全包含 \(\sqrt{n}\) 个数的区间标记,其他暴力求解,两部分的复杂度都为 \(O(\sqrt{n})\),这样每次修改或询问都为 \(O(\sqrt{n})\)
- 时间复杂度:\(O(m\sqrt{n})\)
代码
-
线段树
-
- 懒标记
// Problem: 一个简单的整数问题2
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/244/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=100005;
int n,m,a[N];
struct Tr
{
int l,r;
LL sum,add;
}tr[N<<2];
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u)
{
if(tr[u].add)
{
tr[u<<1].sum+=tr[u].add*(tr[u<<1].r-tr[u<<1].l+1);
tr[u<<1|1].sum+=tr[u].add*(tr[u<<1|1].r-tr[u<<1|1].l+1);
tr[u<<1].add+=tr[u].add,tr[u<<1|1].add+=tr[u].add;
tr[u].add=0;
}
}
void build(int u,int l,int r)
{
tr[u]={l,r,0,0};
if(l==r)
{
tr[u].sum=a[l];
return ;
}
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void change(int u,int l,int r,int d)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].sum+=d*(tr[u].r-tr[u].l+1);
tr[u].add+=d;
return ;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)change(u<<1,l,r,d);
if(r>mid)change(u<<1|1,l,r,d);
pushup(u);
}
LL ask(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r)return tr[u].sum;
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
LL res=0;
if(l<=mid)res+=ask(u<<1,l,r);
if(r>mid)res+=ask(u<<1|1,l,r);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
char op[2];
int l,r,d;
scanf("%s%d%d",op,&l,&r);
if(*op=='Q')printf("%lld\n",ask(1,l,r));
else
{
scanf("%d",&d);
change(1,l,r,d);
}
}
return 0;
}
-
- 标记永久化
// Problem: 一个简单的整数问题2
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/244/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,m;
struct Tr
{
int l,r;
LL add,sum;
}tr[N<<2];
void build(int u,int l,int r)
{
tr[u].l=l,tr[u].r=r;
if(l==r)
{
scanf("%lld",&tr[u].sum);
return ;
}
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void change(int u,int l,int r,int d)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].sum+=(tr[u].r-tr[u].l+1)*d;
tr[u].add+=d;
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)change(u<<1,l,r,d);
if(r>mid)change(u<<1|1,l,r,d);
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum+(tr[u].r-tr[u].l+1)*tr[u].add;
}
LL ask(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r)return tr[u].sum;
LL res=tr[u].add*(min(tr[u].r,r)-max(tr[u].l,l)+1);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)res+=ask(u<<1,l,r);
if(r>mid)res+=ask(u<<1|1,l,r);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--)
{
char op[2];
int l,r,d;
scanf("%s%d%d",op,&l,&r);
if(*op=='Q')printf("%lld\n",ask(1,l,r));
else
{
scanf("%d",&d);
change(1,l,r,d);
}
}
return 0;
}
- zkw线段树
// Problem: 一个简单的整数问题2
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/244/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,m,l,r,d;
char op[2];
int cnt=1;
LL sum[N<<2],add[N<<2];
void build()
{
for(;cnt<=n+1;cnt<<=1);
for(int i=cnt+1;i<=cnt+n;i++)scanf("%lld",&sum[i]);
for(int i=cnt-1;i;i--)sum[i]=sum[i<<1]+sum[i<<1|1];
}
void change(int l,int r,int d)
{
int lnum=0,rnum=0,nnum=1;
for(l=l+cnt-1,r=r+cnt+1;l^r^1;l>>=1,r>>=1,nnum<<=1)
{
sum[l]+=d*lnum,sum[r]+=d*rnum;
if(~l&1)add[l^1]+=d,sum[l^1]+=(LL)d*nnum,lnum+=nnum;
if(r&1)add[r^1]+=d,sum[r^1]+=(LL)d*nnum,rnum+=nnum;
}
for(;l;l>>=1,r>>=1)sum[l]+=(LL)d*lnum,sum[r]+=(LL)d*rnum;
}
LL ask(int l,int r)
{
LL res=0;
int lnum=0,rnum=0,nnum=1;
for(l=l+cnt-1,r=r+cnt+1;l^r^1;l>>=1,r>>=1,nnum<<=1)
{
if(add[l])res+=(LL)add[l]*lnum;
if(add[r])res+=(LL)add[r]*rnum;
if(~l&1)res+=sum[l^1],lnum+=nnum;
if(r&1)res+=sum[r^1],rnum+=nnum;
}
for(;l;l>>=1,r>>=1)res+=(LL)add[l]*lnum+(LL)add[r]*rnum;
return res;
}
int main()
{
scanf("%d%d",&n,&m);
build();
while(m--)
{
scanf("%s%d%d",op,&l,&r);
if(*op=='C')
{
scanf("%d",&d);
change(l,r,d);
}
else
printf("%lld\n",ask(l,r));
}
return 0;
}
- 树状数组
// Problem: 一个简单的整数问题2
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/244/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,m;
LL s[N],tr[2][N];
void add(int ty,int x,int y)
{
for(;x<N;x+=x&-x)tr[ty][x]+=y;
}
LL ask(int ty,int x)
{
LL res=0;
for(;x;x-=x&-x)res+=tr[ty][x];
return res;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&s[i]),s[i]+=s[i-1];
while(m--)
{
char op[2];
int l,r,d;
scanf("%s%d%d",op,&l,&r);
if(*op=='Q')printf("%lld\n",s[r]-s[l-1]+(r+1)*ask(0,r)-ask(1,r)-((l-1+1)*ask(0,l-1)-ask(1,l-1)));
else
{
scanf("%d",&d);
add(0,l,d),add(1,l,l*d);
add(0,r+1,-d),add(1,r+1,(r+1)*-d);
}
}
return 0;
}
- 分块
// Problem: 一个简单的整数问题2
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/244/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=350,M=1e5+5;
int n,m,a[M],len;
LL add[N],sum[N];
int get(int x)
{
return x/len;
}
void change(int l,int r,int d)
{
if(get(l)==get(r))for(int i=l;i<=r;i++)a[i]+=d,sum[get(i)]+=d;
else
{
int i=l,j=r;
while(get(i)==get(l))a[i]+=d,sum[get(i)]+=d,i++;
while(get(j)==get(r))a[j]+=d,sum[get(j)]+=d,j--;
for(int k=get(i);k<=get(j);k++)sum[k]+=len*d,add[k]+=d;
}
}
LL ask(int l,int r)
{
LL res=0;
if(get(l)==get(r))for(int i=l;i<=r;i++)res+=a[i]+add[get(i)];
else
{
int i=l,j=r;
while(get(i)==get(l))res+=a[i]+add[get(i)],i++;
while(get(j)==get(r))res+=a[j]+add[get(j)],j--;
for(int k=get(i);k<=get(j);k++)res+=sum[k];
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
len=sqrt(n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[get(i)]+=a[i];
while(m--)
{
char op[2];
int l,r,d;
scanf("%s%d%d",op,&l,&r);
if(*op=='Q')printf("%lld\n",ask(l,r));
else
{
scanf("%d",&d);
change(l,r,d);
}
}
return 0;
}