Powered by:AB_IN 局外人
强推这篇线段树详解,讲的很全面。
戳这!!
tr[maxn<<2]
大体知道了这些,再看几遍板子,基本就可以写出属于自己的板子了。
带注释的板子。
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2],add_tag[maxn<<2];
// 原数组 线段树 加法lazy_tag标记
ll n,m,p,op1,x,y,k;
inline void pushup(ll i)//更新函数,也可以最大值,最小值。
{
tr[i]=(tr[i<<1]+tr[i<<1|1]);
//比如t[i] = max(t[i<<1],t[i<<1|1]);
}
void bulid(ll i,ll l,ll r)//下面主函数i=1为固定的写法
{
//i为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点
add_tag[i]=0;//tag数组初始化
if(l==r){//如果左右端点相等,即为叶子结点,直接赋值即可
tr[i]=a[l];//注意这里是l
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);//递归构造左子树
bulid(i<<1|1,mid+1,r);//递归构造右子树
pushup(i);//更新父节点,因为先是从上至下建树,再从下至上回溯,pushup相当于往上更新父节点的值。
}
inline void ADD(ll i,ll l,ll r,ll k)//[l,r]区间所有值加k
{
add_tag[i]=(add_tag[i]+k);//这么写方便取余
tr[i]=(tr[i]+(r-l+1)*k);//tr[i]是[l,r]值的和,所以加上(r-l+1)个k
}
inline void pushdown(ll i,ll l,ll r,ll mid)//下传懒标记到左右子树
{
if((!add_tag[i])) return ;
ADD(i<<1,l,mid,add_tag[i]);
ADD(i<<1|1,mid+1,r,add_tag[i]);
add_tag[i]=0; //清除标记
}
inline void update_ADD (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都加上k。
{
//[l,r]为总区间,[x,y]为更新区间,通过不断二分递归[l,r],让l,r在[x,y]中,这样的区间就符合条件组成[x,y]
//[x,y]是定死的,是改变l,r的值
if(l>y||r<x) return;
if(l>=x&&r<=y) return ADD(i,l,r,k);//相当于去执行这项命令
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
update_ADD(i<<1,l,mid,x,y,k);
update_ADD(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];//符合条件时,返回下标对应的值即可。
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
if(x<=mid) res=res+query(i<<1,l,mid,x,y);
if(y>mid) res=res+query(i<<1|1,mid+1,r,x,y);
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
//cin>>n>>m;
n=read();m=read();
rep(i,1,n) a[i]=read();//cin>>a[i];
bulid(1,1,n);
rep(i,1,m){
//cin>>op1;
op1=read();
if(op1==1){
//cin>>x>>y>>k;
x=read();y=read();k=read();
update_ADD(1,1,n,x,y,k);
}
if(op1==2){
//cin>>x>>y;
x=read();y=read();
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
秉持先乘后加的原则。
有乘法时,add_tag,mul_tag,tr
都得变化,因为乘法优先级大,应该先算乘法,所以之前懒标记 加和乘 的值也得乘上。
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2],add_tag[maxn<<2], mul_tag[maxn<<2];
// 原数组 线段树 加法lazy_tag标记 乘法lazy_tag标记
ll n,m,p,op1,x,y,k;
inline void pushup(ll i)//更新函数,也可以最大值,最小值。
{
tr[i]=(tr[i<<1]+tr[i<<1|1])%p;
//比如t[i] = max(t[i<<1],t[i<<1|1]);
}
void bulid(ll i,ll l,ll r)
{
//i为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点
add_tag[i]=0;mul_tag[i]=1;//tag数组初始化
if(l==r){//如果左右端点相等,即为叶子结点,直接赋值即可
tr[i]=a[l];
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);//递归构造左子树
bulid(i<<1|1,mid+1,r);//递归构造右子树
pushup(i);//更新父节点,因为先是从上至下建树,再从下至上回溯,pushup相当于往上更新父节点的值。
}
inline void ADD(ll i,ll l,ll r,ll k)//[l,r]区间所有值加k
{
add_tag[i]=(add_tag[i]+k)%p;//这么写方便取余
tr[i]=(tr[i]+(r-l+1)*k)%p;//tr[i]是[l,r]值的和,所以加上(r-l+1)个k
}
inline void MUL(ll i,ll l,ll r,ll k)//[l,r]区间所有值乘k
{
add_tag[i]=add_tag[i]*k%p;//加法标记也得乘,因为乘法的优先级比加法大。
tr[i]=tr[i]*k%p;
mul_tag[i]=mul_tag[i]*k%p;
}
inline void pushdown(ll i,ll l,ll r,ll mid)//下传懒标记到左右子树
{
//必须先乘后加!!!
if(mul_tag[i]==1&&(!add_tag[i])) return ;
MUL(i<<1,l,mid,mul_tag[i]);
MUL(i<<1|1,mid+1,r,mul_tag[i]);
mul_tag[i]=1;
ADD(i<<1,l,mid,add_tag[i]);
ADD(i<<1|1,mid+1,r,add_tag[i]);
add_tag[i]=0;
}
inline void update_ADD (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都加上k。
{
//[l,r]为总区间,[x,y]为更新区间,通过不断二分递归[l,r],让l,r在[x,y]中,这样的区间就符合条件组成[x,y]
//[x,y]是定死的,是改变l,r的值
if(l>y||r<x) return;
if(l>=x&&r<=y) return ADD(i,l,r,k);
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
update_ADD(i<<1,l,mid,x,y,k);
update_ADD(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
inline void update_MUL (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都乘上k。
{
if(l>y||r<x) return;
if(l>=x&&r<=y) return MUL(i,l,r,k);
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);//下传标记后更新左右子区间
update_MUL(i<<1,l,mid,x,y,k);
update_MUL(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];//符合条件时,返回下标对应的值即可。
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
if(x<=mid) res=(res+query(i<<1,l,mid,x,y))%p;
if(y>mid) res=(res+query(i<<1|1,mid+1,r,x,y))%p;
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
n=read();m=read();p=read();
rep(i,1,n) a[i]=read();
bulid(1,1,n);
rep(i,1,m){
op1=read();
if(op1==1){
x=read();y=read();k=read();
update_MUL(1,1,n,x,y,k);
}
if(op1==2){
x=read();y=read();k=read();
update_ADD(1,1,n,x,y,k);
}
if(op1==3){
x=read();y=read();
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
一开始看操作一,以为是区间查询和区间修改,但看了2,3操作之后又不知道从何下手。所以采取另一种方案——差分。(b[i]
为原数组,a[i]
为差分数组)
l+1
开始,因为l+1
的差分值是b[l+1]-b[l]
。然后维护最大值的绝对值即可。所以要维护的有,区间内 a i a_i ai的和,区间内 ∣ a i ∣ |a_i| ∣ai∣的最大公约数,区间内 ∣ a i ∣ |a_i| ∣ai∣ 的最大值,一个单点更新,两个区间查询。
那这有三个,怎么写才方便好看呢?可以采用结构体。
(最新更新单点更新+区间查询模板)
#include
#define ls i<<1
#define rs i<<1|1
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
typedef long long ll;
using namespace std;
const int maxn=1e5+10;
struct sa{
ll sum;//和
ll gcd;
ll gap;//最大值
}tr[maxn<<2];
ll a[maxn<<2],b[maxn<<2],l,r,n,m,op1,y;
inline void pushup(ll i)
{
tr[i].sum = (tr[ls].sum+tr[rs].sum);
tr[i].gcd = __gcd(tr[ls].gcd,tr[rs].gcd);
tr[i].gap = max(tr[ls].gap,tr[rs].gap);
}
void bulid(ll i,ll l,ll r)
{
if(l==r){
tr[i].sum=a[l];
tr[i].gcd=abs(a[l]);
tr[i].gap=abs(a[l]);
return;
}
ll mid=(l+r)>>1;
bulid(ls,l,mid);
bulid(rs,mid+1,r);
pushup(i);
}
inline void update (ll i,ll l,ll r,ll x,ll y)
{
if(l>x||r<x) return;
if(l==x&&l==r) {
tr[i].sum+=y;
tr[i].gcd=abs(tr[i].sum);
tr[i].gap=abs(tr[i].sum);
return;
}
ll mid=(l+r)>>1;
update(ls,l,mid,x,y);
update(rs,mid+1,r,x,y);
pushup(i);
}
ll query_sum(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i].sum;
ll mid=(l+r)>>1;
if(x<=mid) res=res+query_sum(ls,l,mid,x,y);
if(y>mid) res=res+query_sum(rs,mid+1,r,x,y);
return res;
}
ll query_gcd(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i].gcd;
ll mid=(l+r)>>1;
if(x<=mid) res= __gcd(res,query_gcd(ls,l,mid,x,y));
if(y>mid) res= __gcd(res,query_gcd(rs,mid+1,r,x,y));
return res;
}
ll query_gap(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i].gap;
ll mid=(l+r)>>1;
if(x<=mid) res= max(res,query_gap(ls,l,mid,x,y));
if(y>mid) res= max(res,query_gap(rs,mid+1,r,x,y));
return res;
}
int main()
{
cin>>n>>m;
rep(i,1,n) cin>>b[i],a[i]=b[i]-b[i-1];
bulid(1,1,n);//千万别忘建树啊!!!
rep(i,1,m){
cin>>op1>>l>>r;
if(op1==1){
cin>>y;
update(1,1,n,l,y);
if(r<n) update(1,1,n,r+1,-y);//if条件可加可不加
}
else if(op1==2){
cout<<query_gap(1,1,n,l+1,r)<<endl;
}
else{
cout<<abs(__gcd(query_sum(1,1,n,1,l),query_gcd(1,1,n,l+1,r)))<<endl;//加个绝对值保险一点。。
}
}
return 0;
}
看到等差数列,自然而然想到差分。
通过举例子看看规律吧!
原 数 组 : 1 2 3 4 5 原数组:1 \ 2\ 3\ 4\ 5 原数组:1 2 3 4 5
要在下标[2,4](l=2,r=4
)上加上等差数列 a n = 2 n + 1 a_n=2n+1 an=2n+1(k=2,d=1
)(n应从0开始)
新 数 组 : 1 3 6 9 5 新数组:1 \ 3\ 6\ 9\ 5 新数组:1 3 6 9 5
原 差 分 数 组 : 1 1 1 1 1 原差分数组:1 \ 1\ 1\ 1\ 1 原差分数组:1 1 1 1 1
所以
后 差 分 数 组 : 1 2 3 3 − 4 后差分数组:1 \ 2\ 3\ 3\ -4 后差分数组:1 2 3 3 −4
对比一下就可以看出来 (假设差分数组为a)
则 对 于 l : a [ l ] = a [ l ] + k 对于l\ :a[l]=a[l]+k 对于l :a[l]=a[l]+k
对 于 区 间 ( L , R ] : a [ i ] = a [ i ] + d , i ∈ ( L , R ] 对于区间(L,R]:a[i]=a[i]+d,i∈(L,R] 对于区间(L,R]:a[i]=a[i]+d,i∈(L,R]
对 于 R + 1 : a [ R + 1 ] = a [ R + 1 ] − ( k + ( ( r − l ) ∗ d ) 对于R+1:a[R+1]=a[R+1]-(k+((r-l)*d) 对于R+1:a[R+1]=a[R+1]−(k+((r−l)∗d)
代码孕育而生
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2],add_tag[maxn<<2], mul_tag[maxn<<2],b[maxn<<2];
// 原数组 线段树 加法lazy_tag标记 乘法lazy_tag标记
ll n,m,x,op1,l,r,d,k;
inline void pushup(ll i)//更新函数,也可以最大值,最小值。
{
tr[i]=(tr[i<<1]+tr[i<<1|1]);
//比如t[i] = max(t[i<<1],t[i<<1|1]);
}
void bulid(ll i,ll l,ll r)
{
//i为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点
add_tag[i]=0;mul_tag[i]=1;//tag数组初始化
if(l==r){//如果左右端点相等,即为叶子结点,直接赋值即可
tr[i]=a[l];
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);//递归构造左子树
bulid(i<<1|1,mid+1,r);//递归构造右子树
pushup(i);//更新父节点,因为先是从上至下建树,再从下至上回溯,pushup相当于往上更新父节点的值。
}
inline void ADD(ll i,ll l,ll r,ll k)//[l,r]区间所有值加k
{
add_tag[i]=(add_tag[i]+k);//这么写方便取余
tr[i]=(tr[i]+(r-l+1)*k);//tr[i]是[l,r]值的和,所以加上(r-l+1)个k
}
inline void MUL(ll i,ll l,ll r,ll k)//[l,r]区间所有值乘k
{
add_tag[i]=add_tag[i]*k;//加法标记也得乘,因为乘法的优先级比加法大。
tr[i]=tr[i]*k;
mul_tag[i]=mul_tag[i]*k;
}
inline void pushdown(ll i,ll l,ll r,ll mid)//下传懒标记到左右子树
{
if(mul_tag[i]==1&&(!add_tag[i])) return ;
MUL(i<<1,l,mid,mul_tag[i]);
MUL(i<<1|1,mid+1,r,mul_tag[i]);
mul_tag[i]=1;
ADD(i<<1,l,mid,add_tag[i]);
ADD(i<<1|1,mid+1,r,add_tag[i]);
add_tag[i]=0;
}
inline void update_ADD (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都加上k。
{
//[l,r]为总区间,[x,y]为更新区间,通过不断二分递归[l,r],让l,r在[x,y]中,这样的区间就符合条件组成[x,y]
//[x,y]是定死的,是改变l,r的值
if(l>y||r<x) return;
if(l>=x&&r<=y) return ADD(i,l,r,k);
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
update_ADD(i<<1,l,mid,x,y,k);
update_ADD(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
inline void update_MUL (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都乘上k。
{
if(l>y||r<x) return;
if(l>=x&&r<=y) return MUL(i,l,r,k);
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);//下传标记后更新左右子区间
update_MUL(i<<1,l,mid,x,y,k);
update_MUL(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];//符合条件时,返回下标对应的值即可。
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
if(x<=mid) res=res+query(i<<1,l,mid,x,y);
if(y>mid) res=res+query(i<<1|1,mid+1,r,x,y);
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
cin>>n>>m;
//n=read();m=read();
rep(i,1,n) {
cin>>b[i];
a[i]=b[i]-b[i-1];
}
//b[i]=read();
bulid(1,1,n);
rep(i,1,m){
cin>>op1;
//op1=read();
if(op1==1){
cin>>l>>r>>k>>d;
//l=read();r=read();k=read();d=read();
update_ADD(1,1,n,l,l,k);
if(r>l) update_ADD(1,1,n,l+1,r,d);
if(r!=n) update_ADD(1,1,n,r+1,r+1,-(k+(r-l)*d));
//如果r==n,那么差分数组最后也在维护中,就不用减去数列的末项了。上面一项已经更新完了。可以自己举个例子试试
//其实两个if 不加也无妨
//第二行,如果是进行的单点更新(即l==r),那么第一行就已经完成了。
//第三行,如果r==n了,那么差分数组会多出了一个n+1项,其实数组最多n项,多出来也没人管。
}
if(op1==2){
cin>>x;
//x=read();
printf("%lld\n",query(1,1,n,1,x));
}
}
return 0;
}
看代码注释即可,没用线段树,数据可能有点水吧。
#include
using namespace std;
const int maxn=1e6+10;
int n,x,y,vis[maxn],l,r,op1,ans1,ans2;
/*
vis[i]=0 本身就有树
vis[i]=1 砍完树空了
vis[i]=2 种上树苗
vis[i]=3 砍掉树苗
*/
int main()
{
cin>>l>>n;
for(int i=1;i<=n;i++){
cin>>op1>>x>>y;
if(!op1){
for(int i=x;i<=y;i++){
if(!vis[i]) vis[i]=1;
else if(vis[i]==2){
vis[i]=3;
ans2++;
}
}
}
else{
for(int i=x;i<=y;i++){
if(vis[i]==1) vis[i]=2;
else if(vis[i]==3) vis[i]=2;
}
}
}
for(int i=0;i<=l;i++)
{
if(vis[i]==2)
ans1++;
}
cout<<ans1<<endl<<ans2<<endl;
return 0;
}
没搞懂大佬线段树的意思。
100分的是 O ( n 2 ) O(n^2) O(n2)的dp
200分的得是 O ( n l o g n ) O(nlogn) O(nlogn)的算法,这里用到伟大的STL:upper_bound lower_bound
大佬的题解
注意的是这两个使用时,对象一定得是有序的数组。
如果数组是降序,那么后面得加上greater
。升序就不用加了,默认是升序。
其实就是符合就加进去,不符合就把不合适的换掉。
#include
using namespace std;
const int maxn=1e5+10;
int a[maxn],b1[maxn],b2[maxn];
int n,len1,len2;
int main()
{
while (cin >> a[++n]); n--;
len1=len2=1;
b1[1]=a[1];b2[1]=a[1];
for(int i=2;i<=n;i++){
if(a[i]<=b1[len1]) b1[++len1]=a[i];
else{
int id=upper_bound(b1+1,b1+1+len1,a[i],greater<int>())-b1;
b1[id]=a[i];
}
if(a[i]>b2[len2]) b2[++len2]=a[i];
else{
int id=lower_bound(b2+1,b2+1+len2,a[i])-b2;
b2[id]=a[i];
}
}
cout<<len1<<endl<<len2<<endl;
return 0;
}
单点更新+求最小值
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2];
ll n,m,t,op1,x,y,z;
inline void pushup(ll i)
{
tr[i] = min(tr[i<<1],tr[i<<1|1]);
}
void bulid(ll i,ll l,ll r)
{
if(l==r){
tr[i]=a[l];
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);
bulid(i<<1|1,mid+1,r);
pushup(i);
}
inline void update (ll i,ll l,ll r,ll x,ll y)
{
if(l>x||r<x) return;
if(l==x&&l==r) {tr[i]=y;return;}//单点更新
ll mid=(l+r)>>1;
update(i<<1,l,mid,x,y);
update(i<<1|1,mid+1,r,x,y);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=inf;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];
ll mid=(l+r)>>1;
if(x<=mid) res=min(res,query(i<<1,l,mid,x,y));
if(y>mid) res=min(res,query(i<<1|1,mid+1,r,x,y));
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
n=read();t=read();
//cin>>n>>t;
rep(i,1,n) a[i]=read();
//cin>>a[i];
bulid(1,1,n);
rep(i,1,t){
x=read();y=read();z=read();
//cin>>x>>y>>z;
if(x==1){
update(1,1,n,y,z);
}
if(x==2){
printf("%lld\n",query(1,1,n,y,z));
}
}
return 0;
}
和洛谷模板2相同。
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2],add_tag[maxn<<2], mul_tag[maxn<<2];
// 原数组 线段树 加法lazy_tag标记 乘法lazy_tag标记
ll n,m,p,op1,x,y,k;
inline void pushup(ll i)//更新函数,也可以最大值,最小值。
{
tr[i]=(tr[i<<1]+tr[i<<1|1])%p;
//比如t[i] = max(t[i<<1],t[i<<1|1]);
}
void bulid(ll i,ll l,ll r)
{
//i为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点
add_tag[i]=0;mul_tag[i]=1;//tag数组初始化
if(l==r){//如果左右端点相等,即为叶子结点,直接赋值即可
tr[i]=a[l];
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);//递归构造左子树
bulid(i<<1|1,mid+1,r);//递归构造右子树
pushup(i);//更新父节点,因为先是从上至下建树,再从下至上回溯,pushup相当于往上更新父节点的值。
}
inline void ADD(ll i,ll l,ll r,ll k)//[l,r]区间所有值加k
{
add_tag[i]=(add_tag[i]+k)%p;//这么写方便取余
tr[i]=(tr[i]+(r-l+1)*k)%p;//tr[i]是[l,r]值的和,所以加上(r-l+1)个k
}
inline void MUL(ll i,ll l,ll r,ll k)//[l,r]区间所有值乘k
{
add_tag[i]=add_tag[i]*k%p;//加法标记也得乘,因为乘法的优先级比加法大。
tr[i]=tr[i]*k%p;
mul_tag[i]=mul_tag[i]*k%p;
}
inline void pushdown(ll i,ll l,ll r,ll mid)//下传懒标记到左右子树
{
//必须先乘后加!!!
if(mul_tag[i]==1&&(!add_tag[i])) return ;
MUL(i<<1,l,mid,mul_tag[i]);
MUL(i<<1|1,mid+1,r,mul_tag[i]);
mul_tag[i]=1;
ADD(i<<1,l,mid,add_tag[i]);
ADD(i<<1|1,mid+1,r,add_tag[i]);
add_tag[i]=0;
}
inline void update_ADD (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都加上k。
{
//[l,r]为总区间,[x,y]为更新区间,通过不断二分递归[l,r],让l,r在[x,y]中,这样的区间就符合条件组成[x,y]
//[x,y]是定死的,是改变l,r的值
if(l>y||r<x) return;
if(l>=x&&r<=y) return ADD(i,l,r,k);
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
update_ADD(i<<1,l,mid,x,y,k);
update_ADD(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
inline void update_MUL (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都乘上k。
{
if(l>y||r<x) return;
if(l>=x&&r<=y) return MUL(i,l,r,k);
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);//下传标记后更新左右子区间
update_MUL(i<<1,l,mid,x,y,k);
update_MUL(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];//符合条件时,返回下标对应的值即可。
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
if(x<=mid) res=(res+query(i<<1,l,mid,x,y))%p;
if(y>mid) res=(res+query(i<<1|1,mid+1,r,x,y))%p;
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
//cin>>n>>p;
n=read();p=read();
rep(i,1,n) a[i]=read();
bulid(1,1,n);
//cin>>m;
m=read();
rep(i,1,m){
//cin>>op1;
op1=read();
if(op1==1){
//cin>>x>>y>>k;
x=read();y=read();k=read();
update_MUL(1,1,n,x,y,k);
}
if(op1==2){
//cin>>x>>y>>k;
x=read();y=read();k=read();
update_ADD(1,1,n,x,y,k);
}
if(op1==3){
//cin>>x>>y;
x=read();y=read();
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
维护一个区间的最大值+单点修改+区间查询。
当一个区间的最大值为1时,就不用开方了。(要不然浪费时间)
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2],mx[maxn<<2];
ll n,m,p,op1,x,y,k;
inline void pushup(ll i)
{
tr[i]=(tr[i<<1]+tr[i<<1|1]);
mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
void bulid(ll i,ll l,ll r)
{
if(l==r){
tr[i]=a[l];
mx[i]=a[l];
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);
bulid(i<<1|1,mid+1,r);
pushup(i);。
}
inline void SQRT(ll i,ll l,ll r)
{
tr[i]=sqrt(tr[i]);
mx[i]=tr[i];
}
inline void update_SQRT (ll i,ll l,ll r,ll x,ll y)
{
if(mx[i]<=1) return;
if(l>y||r<x) return;
if(l==r) return SQRT(i,l,r);
ll mid=(l+r)>>1;
update_SQRT(i<<1,l,mid,x,y);
update_SQRT(i<<1|1,mid+1,r,x,y);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];
ll mid=(l+r)>>1;
if(x<=mid) res=res+query(i<<1,l,mid,x,y);
if(y>mid) res=res+query(i<<1|1,mid+1,r,x,y);
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
//cin>>n;
n=read();
rep(i,1,n) a[i]=read();
bulid(1,1,n);
//cin>>m;
m=read();
rep(i,1,m){
//cin>>op1;
op1=read();
if(op1==2){
//cin>>x>>y;
x=read();y=read();
update_SQRT(1,1,n,x,y);
}
if(op1==1){
//cin>>x>>y;
x=read();y=read();
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
同模板1
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2];
ll n,m,t,op1,x,y,z;
inline void pushup(ll i)
{
tr[i] = tr[i<<1]+tr[i<<1|1];
}
void bulid(ll i,ll l,ll r)
{
if(l==r){
tr[i]=a[l];
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);
bulid(i<<1|1,mid+1,r);
pushup(i);
}
inline void update (ll i,ll l,ll r,ll x,ll y)
{
if(l>x||r<x) return;
if(l==x&&l==r) {tr[i]+=y;return;}
ll mid=(l+r)>>1;
update(i<<1,l,mid,x,y);
update(i<<1|1,mid+1,r,x,y);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];
ll mid=(l+r)>>1;
if(x<=mid) res=res+query(i<<1,l,mid,x,y);
if(y>mid) res=res+query(i<<1|1,mid+1,r,x,y);
return res;
}
namespace IO{
char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
inline char gc(){
if(ip!=ip_)return *ip++;
ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
return ip==ip_?EOF:*ip++;
}
inline void pc(char c){
if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
*op++=c;
}
inline ll read(){
register ll x=0,ch=gc(),w=1;
for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
return w*x;
}
template<class I>
inline void write(I x){
if(x<0)pc('-'),x=-x;
if(x>9)write(x/10);pc(x%10+'0');
}
class flusher_{
public:
~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
}IO_flusher;
}
using namespace IO;
int main()
{
n=read();t=read();
//cin>>n>>t;
bulid(1,1,n);
rep(i,1,t){
x=read();y=read();z=read();
//cin>>x>>y>>z;
if(x==0){
update(1,1,n,y,z);
}
if(x==1){
printf("%lld\n",query(1,1,n,y,z));
}
}
return 0;
}
一开始想的这题是,区间更新+找最大值。但其实是不对的。
如果在(1,3) (4,6)种树,那么查询(2,4)的最大值还是1。
正解是括号序列的思想。
用两个树状数组维护每个节点左右端点个数,端点数即是该点上覆盖的线段。
用r前面的左括号数量 − - −l前面的右括号数量即可。
附上一张潦草的图。
#include
using namespace std;
#define lowbit(x) ((x) &-(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
int n,m,left_tree[1000000],right_tree[1000000],l,r,op1;
inline void add_l(int x,int d)
{
while(x<=n){
left_tree[x]+=d;
x+=lowbit(x);
}
}
inline void add_r(int x,int d)
{
while(x<=n){
right_tree[x]+=d;
x+=lowbit(x);
}
}
int sum_l(int x)
{
int sum=0;
while(x>0){
sum+=left_tree[x];
x-=lowbit(x);
}
return sum;
}
int sum_r(int x)
{
int sum=0;
while(x>0){
sum+=right_tree[x];
x-=lowbit(x);
}
return sum;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&op1,&l,&r);
if(op1==1){
add_l(l,1);
add_r(r,1);
}
else printf("%d\n",sum_l(r)-sum_r(l-1));
}
}
卡了我一手快读,不明白怎么回事。
#include
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll a[maxn<<2],tr[maxn<<2],add_tag[maxn<<2];
// 原数组 线段树 加法lazy_tag标记
ll n,m,p,op1,x,y,k;
inline void pushup(ll i)//更新函数,也可以最大值,最小值。
{
tr[i]=(tr[i<<1]+tr[i<<1|1]);
//比如t[i] = max(t[i<<1],t[i<<1|1]);
}
void bulid(ll i,ll l,ll r)//下面主函数i=1为固定的写法
{
//i为当前需要建立的结点,l为当前需要建立区间的左端点,r则为右端点
add_tag[i]=0;//tag数组初始化
if(l==r){//如果左右端点相等,即为叶子结点,直接赋值即可
tr[i]=a[l];//注意这里是l
return;
}
ll mid=(l+r)>>1;
bulid(i<<1,l,mid);//递归构造左子树
bulid(i<<1|1,mid+1,r);//递归构造右子树
pushup(i);//更新父节点,因为先是从上至下建树,再从下至上回溯,pushup相当于往上更新父节点的值。
}
inline void ADD(ll i,ll l,ll r,ll k)//[l,r]区间所有值加k
{
add_tag[i]=(add_tag[i]+k);//这么写方便取余
tr[i]=(tr[i]+(r-l+1)*k);//tr[i]是[l,r]值的和,所以加上(r-l+1)个k
}
inline void pushdown(ll i,ll l,ll r,ll mid)//下传懒标记到左右子树
{
if((!add_tag[i])) return ;
ADD(i<<1,l,mid,add_tag[i]);
ADD(i<<1|1,mid+1,r,add_tag[i]);
add_tag[i]=0; //清除标记
}
inline void update_ADD (ll i,ll l,ll r,ll x,ll y,ll k)//更新[x,y]区间,都加上k。
{
//[l,r]为总区间,[x,y]为更新区间,通过不断二分递归[l,r],让l,r在[x,y]中,这样的区间就符合条件组成[x,y]
//[x,y]是定死的,是改变l,r的值
if(l>y||r<x) return;
if(l>=x&&r<=y) return ADD(i,l,r,k);//相当于去执行这项命令
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
update_ADD(i<<1,l,mid,x,y,k);
update_ADD(i<<1|1,mid+1,r,x,y,k);
pushup(i);
}
ll query(ll i,ll l,ll r,ll x,ll y)
{
ll res=0;
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return tr[i];//符合条件时,返回下标对应的值即可。
ll mid=(l+r)>>1;
pushdown(i,l,r,mid);
if(x<=mid) res=res+query(i<<1,l,mid,x,y);
if(y>mid) res=res+query(i<<1|1,mid+1,r,x,y);
return res;
}
int main()
{
while(~scanf("%lld%lld",&n,&m)){
memset(tr,0,sizeof(tr));
memset(add_tag,0,sizeof(add_tag));
memset(a,0,sizeof(a));
rep(i,1,m){
//cin>>x>>y>>k;
scanf("%lld%lld%lld",&x,&y,&k);
update_ADD(1,1,n,x,y,k);
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
完结。