重温一下线段树,听说蓝桥杯线段树考的比较多,过来复习一下,一个线段树的板子题。
题目大意:
支持区间加、区间乘、区间查询
要求时间复杂度为nlog(n)级别
显然线段树(树状数组忘记怎么写了QAQ~ )
如果只有区间加法或者只有区间乘法,本题将直接做标记,下放标记即可,单次修改或查询最坏情况下时间复杂度为树的高度,即:log(n),总体的时间复杂度为mlog(n)
而本题有加法和乘法两种标记,所以本题的核心就是怎么处理加法和乘法标记的关系
如上图,可以解读为:
(x+3)* 5 ---------------------------------------(1)
(x*5)+ 3 ---------------------------------------(2)
为了避免产生歧义我们必须规定一个先后顺序。
假设我们先算的是加法
①若: + 3 比 *5 先运行,则直接计算(1)式
②若: *5 比 + 3 先运行
此时, jia 无可避免的会乘以 cheng 很难计算出正确的数值
假设我们先算的是乘法
①若: + 3 比 *5 先运行,则我们在 * 5 的时候,对于还没有下方的 + 3 也要进行 * 5 的计算即将(1)式拆开
x * 5 + 3 * 5 ---------------------------------------(3)
②若: *5 比 + 3 先运行,则直接计算(2)式
故,我们先处理乘法标记
xf_cheng(root); //先下放
xf_jia(root); //后下放
下放乘法标记
void xf_cheng(zt *root)
{
root->lift->cheng *= root->cheng; //向左
root->lift->cheng %= p;
root->right->cheng *= root->cheng; //向右
root->right->cheng %= p;
root->lift->jia *= root->cheng; //处理之前的加法,类似上文(1)式
root->lift->jia %= p;
root->right->jia *= root->cheng;
root->right->jia %= p;
root->lift->sum *= root->cheng; //处理上文x,此处用sum表示的
root->lift->sum %= p;
root->right->sum *= root->cheng;
root->right->sum %= p;
root->cheng = 1; //下放后清除标记为1
}
下放加法标记
void xf_jia(zt *root)
{
root->lift->jia += root->jia;
root->right->jia += root->jia;
root->lift->sum += root->jia * root->lift->sz;
root->lift->sum %= p;
root->right->sum += root->jia * root->right->sz;
root->right->sum %= p;
root->jia = 0;
}
完整代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 4e5 + 5;
struct zt
{
ll l,r,sum,sz,jia,cheng;
zt *lift,*right;
};
zt tree[N];
int n,m,p,cnt,a,x,y,k;
void xf_cheng(zt *root)
{
root->lift->cheng *= root->cheng;
root->lift->cheng %= p;
root->right->cheng *= root->cheng;
root->right->cheng %= p;
root->lift->jia *= root->cheng;
root->lift->jia %= p;
root->right->jia *= root->cheng;
root->right->jia %= p;
root->lift->sum *= root->cheng;
root->lift->sum %= p;
root->right->sum *= root->cheng;
root->right->sum %= p;
root->cheng = 1;
}
void xf_jia(zt *root)
{
root->lift->jia += root->jia;
root->right->jia += root->jia;
root->lift->sum += root->jia * root->lift->sz;
root->lift->sum %= p;
root->right->sum += root->jia * root->right->sz;
root->right->sum %= p;
root->jia = 0;
}
void build_tree(zt *root,ll l,ll r)
{
root->sum = 0;root->l = l;root->r = r;
root->sz = r - l + 1;root->jia = 0;root->cheng = 1;
if(l == r) return;
root->lift = &tree[++cnt];
root->right = &tree[++cnt];
ll mid = l + r >> 1;
build_tree(root->lift,l,mid);
build_tree(root->right,mid + 1,r);
}
void add(zt *root,ll l,ll r,ll ql,ll qr,ll x)
{
if(l >= ql&&qr >= r)
{
root->sum += root->sz * x;
root->sum %= p;
root->jia += x;
root->jia %= p;
return;
}
xf_cheng(root);
xf_jia(root);
ll mid = l + r >> 1;
if(ql <= mid) add(root->lift,l,mid,ql,qr,x);
if(qr > mid) add(root->right,mid + 1,r,ql,qr,x);
root->sum = root->lift->sum + root->right->sum;
root->sum %= p;
}
void cheng(zt *root,ll l,ll r,ll ql,ll qr,ll x)
{
if(l >= ql&&qr >= r)
{
root->sum *= x;
root->sum %= p;
root->cheng *= x;
root->cheng %= p;
root->jia *= x;
root->jia %= p;
return;
}
xf_cheng(root);
xf_jia(root);
ll mid = l + r >> 1;
if(ql <= mid) cheng(root->lift,l,mid,ql,qr,x);
if(qr > mid) cheng(root->right,mid + 1,r,ql,qr,x);
root->sum = root->lift->sum + root->right->sum;
root->sum %= p;
}
ll sum(zt *root,ll l,ll r,ll ql,ll qr)
{
if(l >= ql&&qr >= r)
return root->sum % p;
xf_cheng(root);
xf_jia(root);
ll mid = l + r >> 1,ans = 0;
if(ql <= mid) ans = sum(root->lift,l,mid,ql,qr);
if(qr > mid) ans += sum(root->right,mid + 1,r,ql,qr);
root->sum = root->lift->sum + root->right->sum;
root->sum %= p;
return ans % p;
}
int main()
{
cin>>n>>m>>p;
build_tree(tree,1,n);
for(int i = 1;i <= n;i ++)
{
cin>>a;
add(tree,1,n,i,i,a);
}
for(int i = 1;i <= m;i ++)
{
cin>>a;
if(a == 3)
{
cin>>x>>y;
cout<<sum(tree,1,n,x,y) % p<<'\n';
}
else
{
cin>>x>>y>>k;
if(a == 2)
add(tree,1,n,x,y,k);
else
cheng(tree,1,n,x,y,k);
}
}
}