哇,虽然上一篇博客是在十月,但感觉好像有三个月没写博客的样子。
主要是之前太菜,不愿面对,加上最近犯懒,没有坚持写。
P3372 【模板】线段树 1
题意
题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
输入格式
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和
输出格式
输出包含若干行整数,即为所有操作2的结果。
代码
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=100005;
typedef long long ll;
struct SegTreeNode
{
ll val;
ll lazyTag;//延迟标记:储存节点的修改情况
SegTreeNode()
{
val=0;
lazyTag=0;
}
} segTree[maxn<<2];//完全二叉树(有没用到的节点),一倍可能炸
inline void pushup(int root)
{
segTree[root].val=segTree[root*2].val+segTree[root*2+1].val;
}
inline void pushdown(int root,int l,int r)//当前节点标志域向孩子节点传递
{//root为当前节点下标
if(segTree[root].lazyTag)
{
segTree[root*2].lazyTag+=segTree[root].lazyTag;
segTree[root*2+1].lazyTag+=segTree[root].lazyTag;
int mid=(l+r)>>1;
segTree[root*2].val+=segTree[root].lazyTag*(mid-l+1);
segTree[root*2+1].val+=segTree[root].lazyTag*(r-mid);
segTree[root].lazyTag=0;//消掉该点的标记
}
}
void build(int root,ll arr[],int ist,int ied)
{//当前根节点
segTree[root].lazyTag=0;
if(ist==ied)//叶子节点
segTree[root].val=arr[ist];
else
{
int mid=(ist+ied)>>1;
build(root*2,arr,ist,mid);
build(root*2+1,arr,mid+1,ied);
pushup(root);
}
}
void update(int root,int nst,int ned,int ust,int ued,ll num)
{//nst:当前区间,ust:更新区间
if(ust>ned||ued<nst)
return;
if(ust<=nst&&ned<=ued)//子区间更新完成,
{
segTree[root].lazyTag+=num;//更新值记录在lazyTag
segTree[root].val+=(ned-nst+1)*num;
return;
}
// printf("l=%d,r=%d\n",nst,ned);
pushdown(root,nst,ned);//标记向下传递
int mid=(nst+ned)>>1;
// cout<<"HERE1"<<endl;
// system("pause");
update(root*2,nst,mid,ust,ued,num);
// cout<<"HERE2"<<endl;
update(root*2+1,mid+1,ned,ust,ued,num);
// cout<<"!!!"<<endl;
pushup(root);
}
ll query(int root,int nst,int ned,int qst,int qed)//区间查询
{//n:当前区间,q:查询区间
// cout<<"HAHAHA"<<endl;
if(qst>ned||qed<nst)//没有交集
return 0;
if(qst<=nst&&qed>=ned)//覆盖该区间,返回此区间值
return segTree[root].val;
pushdown(root,nst,ned);
int mid=(nst+ned)>>1;
return query(root*2,nst,mid,qst,qed)+query(root*2+1,mid+1,ned,qst,qed);
// cout<<"???"<<endl;
}
ll a[maxn];
int main()
{
int n,m,x,y;
ll k;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,a,1,n);
while(m--)
{
int flag;
scanf("%d",&flag);
if(flag==1)
{
scanf("%d%d%lld",&x,&y,&k);
update(1,1,n,x,y,k);
}
else
{
scanf("%d%d",&x,&y);
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
很裸的线段树模板题,之前区间端点的判断写错,无限递归疯狂RE(应该是线段树的常见错误了)。
P3373 【模板】线段树 2
题意
题目描述
如题,已知一个数列,你需要进行下面三种操作:
1.将某区间每一个数乘上x
2.将某区间每一个数加上x
3.求出某区间每一个数的和
输入格式
第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k
操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k
操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果
输出格式
输出包含若干行整数,即为所有操作3的结果。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100005;
struct node
{
ll val,add,mul;
} tree[maxn<<2];
ll a[maxn],p;
inline void pushup(int root)
{
tree[root].val=(tree[root<<1].val+tree[(root<<1)+1].val)%p;
}
void build(int root,ll arr[],int l,int r)
{
tree[root].add=0;
tree[root].mul=1;
if(l==r)
tree[root].val=arr[l];
else{
int mid=l+r>>1;
build(root<<1,arr,l,mid);
build((root<<1)+1,arr,mid+1,r);
pushup(root);
}
}
inline void pushdown(int root,int l,int r)
{
int mid=l+r>>1;
//子节点的值=原值*父节点mul+父节点add*区间长度
tree[root<<1].val=((tree[root<<1].val*tree[root].mul)%p+(tree[root].add*(mid-l+1))%p)%p;//更新子节点的值
tree[(root<<1)+1].val=((tree[(root<<1)+1].val*tree[root].mul)%p+(tree[root].add*(r-mid))%p)%p;
//维护子节点的lazyTag,子节点mul=与父结点mul累乘
tree[root<<1].mul=(tree[root<<1].mul*tree[root].mul)%p;
tree[(root<<1)+1].mul=(tree[(root<<1)+1].mul*tree[root].mul)%p;
//子节点add=add*父节点mul+父节点add
tree[root<<1].add=((tree[root<<1].add*tree[root].mul)%p+tree[root].add)%p;
tree[(root<<1)+1].add=((tree[(root<<1)+1].add*tree[root].mul)%p+tree[root].add)%p;
tree[root].add=0;
tree[root].mul=1;
}
void update(int root,int nst,int ned,int ust,int ued,ll addnum,ll mulnum)
{
if(nst>ued||ust>ned)
return;
if(ust<=nst&&ned<=ued)
{
tree[root].val=((tree[root].val*mulnum)%p+(addnum*(ned-nst+1))%p)%p;
tree[root].add=(tree[root].add*mulnum+addnum)%p;
tree[root].mul=(tree[root].mul*mulnum)%p;
return;
}
pushdown(root,nst,ned);
int mid=nst+ned>>1;
update(root<<1,nst,mid,ust,ued,addnum,mulnum);
update((root<<1)+1,mid+1,ned,ust,ued,addnum,mulnum);
pushup(root);
}
ll query(int root,int nst,int ned,int qst,int qed)
{
if(qst>ned||nst>qed)
return 0;
if(qst<=nst&&ned<=qed)
{
return tree[root].val%p;
}
pushdown(root,nst,ned);
int mid=nst+ned>>1;
return (query(root<<1,nst,mid,qst,qed)+query((root<<1)+1,mid+1,ned,qst,qed))%p;
}
int main()
{
// freopen("P3373.in","r",stdin);
int n,m;
scanf("%d%d%lld",&n,&m,&p);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,a,1,n);
// cout<<"build finished"<<endl;
while(m--)
{
int flag,x,y;
ll k;
scanf("%d",&flag);
if(flag==1)
{
scanf("%d%d%lld",&x,&y,&k);
// cout<<"flag="<<flag<<endl;
update(1,1,n,x,y,0,k);
// cout<<"flag1"<<endl;
}
else if(flag==2)
{
scanf("%d%d%lld",&x,&y,&k);
// cout<<"flag="<<flag<<endl;
update(1,1,n,x,y,k,1);
// cout<<"flag2"<<endl;
}
else{
scanf("%d%d",&x,&y);
printf("%lld\n",query(1,1,n,x,y)%p);
}
}
return 0;
}
这道题混合了区间加/乘上一个数,所以怎么写好pushdown,如何处理加法与乘法的优先级是个难点。
从下午三点调到晚上八点多才调好。
2019年11月6日20点41分