那是一个春光明媚的下午
我们被锁在机房里,被逼学树状数组
邪恶的老师
学懂了才能走!
老师你看我核善的微笑
然后于痛苦和绝望中
我们学会了树状数组&&线段树(只是一点很肤浅的)
然后,
现在谈谈树状数组
学过的都知道
也很轻松就能推出
单点修改-区间查询
区间修改-单点查询
区间修改-区间查询
,我就推不出来了orz(qwq
先说说
单点修改-区间查询
而树状数组代码大概长这样
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 50001;
int n,tot,Bit[M];
inline int lowbit(int x)
{
return x & -x;
}
inline void update(int Index,int delta)
{
int i;
for(i = Index;i <= n;i += lowbit(i))
Bit[i] += delta;
}
inline int sum(const int Index)
{
int i,Sum = 0;
for(i = Index;i >0;i -= lowbit(i))
Sum += Bit[i];
return Sum;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
tot++;
printf("Case %d:
",tot);
int i;
scanf("%d",&n);
for(i = 1;i <= n;i++)
{
int soldier;
scanf("%d",&soldier);
update(i,soldier);
}
char s[15];
while(1)
{
scanf("%s",s);
if(s[0] == 'E')
break;
int u,v;
scanf("%d %d",&u,&v);
if(s[0] == 'A')
update(u,v);
if(s[0] == 'S')
update(u,-v);
if(s[0] == 'Q')
printf("%d
",sum(v) - sum(u - 1));
}
memset(Bit,0,sizeof(Bit));
}
return 0;
}
再谈谈
区间修改-单点查询
- 其实
- 它和
单点修改-区间查询
很像
就这样搞搞
一个序列
↓ ↓
1 2 3 4 5 6 7 8 9
要使1 - 7
区间内加上5
update(1,5);
update(8,-5);
应对查询就
sum(Index);
ok
⇝ ⇝ ⇝ ⇝就有经验
区间修改-区间查询
蒟蒻的我
再也推不出来了
看下表
6:00
(⊙o⊙)…
看来是走不了了
不行,我的fate/stay night[unlimited blade works]还没看完
我的saber
我的手办
祈的歌我还没有听够
我怎么会就这样陨落
这时,一声神秘的voice出现
少年,学线段树吧
于是就有了...
首先建树
inline void tree_built(int l,int r,int k)
{
if(l == r)
{
tree[k] = num[l];
return;
}
int mid = l + r >> 1;
k = k << 1;
//<<左移一位(2进制),位运算,优先级低于=-*/
//>>同理
tree_built(l,mid,k);
tree_built(mid + 1,r,k ^ 1);
tree[k >> 1] = tree[k] + tree[k ^ 1];
//因为此时k的二进制末位一定为0,0 ^ 1 = 0 + 1
}
设有xϵ{ (num_1),...(num_n)}
1 ~ 8
↙ ↘
1 ~ 4
5 ~ 8
↙ ↘ ↙ ↘
1~2
3~4
5~6
7~8
1
2
3
4
5
6
7
8
可见每次修改一个区间o(log n),
查询亦为o(log n)
那为什么
编号为k的左子树编号为k << 1
右子树编号为k << 1 ^ 1 ??
k = k << 1;
tree_built(l,mid,k);
tree_built(mid + 1,r,k ^ 1);
证明:
第q(q >= 2,前两层手推)层的从左往右第a个区间
编号为
(2_0) + (2_1) + (2_2) + ··· +(2_{q - 2}) + a
现在...
这个区间的左儿子的编号
这个区间的编号×2
⇔ ((2_0) + (2_1) + (2_2) + ··· +(2_{q - 2}) + a)* 2 = (2_0) + (2_1) + (2_2) + ··· +(2_{q - 1}) + (a - 1) * 2 + 1
⇔ 2 * a = 2 * a
得证.
然后时间复杂度大约为o(树的结点数)
然后求和
int query(int l,int r,int k)
{
if(r <= Right&&l >= Left)
return tree[k];
int mid = l + r >> 1;
int k = k << 1;
int sum = 0;
if(Left <= mid)
sum = modify(l,mid,k);
if(Right > mid)
sum += modify(mid + 1,r,k ^ 1);
return sum;
}
清晰易懂,不用解释
修改
inline void modify(int k,int l,int r,int delta)
{
if(Left <= l&&Right >= r)
{
Add(k,l,r,delta);
return;
}
int mid = l + r >> 1;
PushDown(k,l,r,mid);
//如果,要往下,先将这里的修改值传下去
if(Left <= mid)
modify(k << 1,l,mid,delta);
if(mid < Right)
modify(k << 1 ^ 1,mid + 1,r,delta);
tree[k] = tree[k << 1] + tree[k << 1 ^ 1];
}
上面就有两个函数
Add&PushDown
这里用的是标记下传的方法
inline void Add(int k,int l,int r,int delta)
{
add[k] += delta;
tree[k] += ll(delta) * (r - l + 1);
}
inline void PushDown(int k,int l,int r,int mid)
{
if(add[k] == 0)
return;
Add(k << 1,l,mid,add[k]);
Add(k << 1 ^ 1,mid + 1,r,add[k]);
add[k] = 0;
}
求和
inline ll query(int k,int l,int r)
{
if(r <= Right&&l >= Left)
return tree[k];
int mid = l + r >> 1;
PushDown(k,l,r,mid);
//唯一值得注意的就是这里也需要下传
//因为上一次可能修改了1~n,只有tree[1]被修改了,而查询的却是1~1
ll ans = 0;
if(mid >= Left)
ans = query(k << 1,l,mid);
if(Right > mid)
ans += query(k << 1 ^ 1,mid + 1,r);
return ans;
}
合起来
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
ll tree[MAXN * 4];
int num[MAXN + 1],add[MAXN * 4],Left,Right;
inline void Read(int *x)
{
*x = 0;
char a = getchar();
bool f = 0;
while(a > '9'||a < '0') {if(a == '-') f = 1;a = getchar();}
while(a <= '9'&&a >= '0') {*x = *x * 10 + a - '0';a = getchar();}
if(f)
*x *= -1;
}
inline void tree_built(int l,int r,int k)
{
if(l == r)
{
tree[k] = num[l];
return;
}
int mid = l + r >> 1;
k = k << 1;
tree_built(l,mid,k);
tree_built(mid + 1,r,k ^ 1);
tree[k >> 1] = tree[k] + tree[k ^ 1];
}
inline void Add(int k,int l,int r,int delta)
{
add[k] += delta;
tree[k] += ll(delta) * (r - l + 1);
}
inline void PushDown(int k,int l,int r,int mid)
{
if(add[k] == 0)
return;
Add(k << 1,l,mid,add[k]);
Add(k << 1 ^ 1,mid + 1,r,add[k]);
add[k] = 0;
}
inline ll query(int k,int l,int r)
{
if(r <= Right&&l >= Left)
return tree[k];
int mid = l + r >> 1;
PushDown(k,l,r,mid);
ll ans = 0;
if(mid >= Left)
ans = query(k << 1,l,mid);
if(Right > mid)
ans += query(k << 1 ^ 1,mid + 1,r);
return ans;
}
inline void modify(int k,int l,int r,int delta)
{
if(Left <= l&&Right >= r)
{
Add(k,l,r,delta);
return;
}
int mid = l + r >> 1;
PushDown(k,l,r,mid);
if(Left <= mid)
modify(k << 1,l,mid,delta);
if(mid < Right)
modify(k << 1 ^ 1,mid + 1,r,delta);
tree[k] = tree[k << 1] + tree[k << 1 ^ 1];
}
int main()
{
int i,n,m;
Read(&n),Read(&m);
for(i = 1;i <= n;i++)
scanf("%d",&num[i]);
tree_built(1,n,1);
while(m--)
{
int Case;
Read(&Case),Read(&Left),Read(&Right);
if(Case == 1)
{
int delta;
Read(&delta);
modify(1,1,n,delta);
} else {
ll ans = query(1,1,n);
printf("%lld
",ans);
}
}
return 0;
}
然后这道题就可以搞搞了
还有这道
那么,难一点的
#include <cstdio>
#include <cstring>
const int MAXN = 100001;
int n,m,p,num[MAXN],Left,Right;
long long add[MAXN * 4],add2[MAXN * 4],tree[MAXN * 4];
inline void tree_build(int l,int r,int k)
{
add2[k] = 1;
if(l == r)
{
tree[k] = num[l];
return;
}
int mid = l + r >> 1;
tree_build(l,mid,k << 1);
tree_build(mid + 1,r,k << 1 ^ 1);
tree[k] = (tree[k << 1] + tree[k << 1 ^ 1]) % p;
}
inline void Add(int l,int r,int k,long long delta,bool Case)
{
if(!Case)
{
tree[k] = (tree[k] + (r - l + 1) * (delta % p)) % p;
add[k] = (delta % p + add[k]) % p;
} else {
tree[k] = tree[k] % p * delta % p;
add2[k] = add2[k] * delta % p;
add[k] = add[k] * delta % p;
}
}
inline void PushDown(int l,int r,int k)
{
int mid = l + r >> 1;
Add(l,mid,k << 1,add2[k],1);
Add(l,mid,k << 1,add[k],0);
Add(mid + 1,r,k << 1 ^ 1,add2[k],1);
Add(mid + 1,r,k << 1 ^ 1,add[k],0);
add2[k] = 1;
add[k] = 0;
}
inline long long query(int l,int r,int k)
{
if(Left <= l&&r <= Right)
return tree[k];
int mid = l + r >> 1;
long long ans = 0;
PushDown(l,r,k);
if(Left <= mid)
ans = query(l,mid,k << 1);
if(mid < Right)
ans += query(mid + 1,r,k << 1 ^ 1);
return ans % p;
}
inline void modify(int l,int r,int k,long long delta,bool Case)
{
if(Left <= l&&r <= Right)
{
Add(l,r,k,delta,Case);
return;
}
PushDown(l,r,k);
int mid = l + r >> 1;
if(Left <= mid)
modify(l,mid,k << 1,delta,Case);
if(mid < Right)
modify(mid + 1,r,k << 1 ^ 1,delta,Case);
tree[k] = (tree[k << 1] + tree[k << 1 ^ 1]) % p;
}
int main()
{
int i;
scanf("%d%d%d",&n,&m,&p);
for(i = 1;i <= n;i++)
scanf("%lld",&num[i]);
tree_build(1,n,1);
for(i = 1;i <= m;i++)
{
int Case;
long long delta;
scanf("%d%d%d",&Case,&Left,&Right);
if(Case == 3)
printf("%lld
",query(1,n,1));
else {
scanf("%lld",&delta);
if(Case == 2) modify(1,n,1,delta,0);
else modify(1,n,1,delta,1);
}
}
return 0;
}