线段树(不要看,我自己都看不太懂)
距离蓝桥杯只有不到四周,不知道学这个还有没有用,理解模板都费劲了,估计也不能举一反三吧。。。
//先看概念,就是在整个区间上建立二叉树
//设 区间为 a[N] , 线段树数组为 tree[N]
//则从顶部递归建立二叉树
inline void build(int i,int l,int r)
{
if(l==r)//左右边界相等则tree[ i ]==a[ i ]
{
tree[i]=a[l];
return;
}
//对左右两边分别建树
int mid=(l+r)>>1;
build(i*2,l,mid),build(i*2+1,mid+1,r);
//建完之后根据左右两边的结果更新当前 l ~ r 的结果
push(i);
}
//可以看到还需要一个 push 函数进行左右两边建树后的更新
inline void push(int i)
{
//根据需要,看是找什么,可能是最大值、累加、次大值。。。。
//不同的情况的 push 不同
tree[i]=max(tree[i*2],tree[i*2+1]);
}
//懒惰标签,即每次找到管辖区域在区间内的,就直接更新节点,并进行标记,下次有需要再查到他的子节点的话就在根据标签往下更新后再查询
inline void spread(int i)
{
if(tag[i])
{
tree[i*2]+=tag[i];
tree[i*2+1]+=tag[i];
tag[i]=0;
}
}
//建完树了,我们肯定是要用来查什么东西,就要有 ask 函数
//这里仅以最大值距离,还是那句话,情况不同,ask 也不同
inline int ask(int i,int s,int t,int l,int r)
{
if(l<=s&&t<=r)
{
return tree[i];
}
spread(int i,int s,int t);
if(r<s||t<l) return 0;//处理越界
int mid=(s+t)>>1;//求mid用的是 i 的管辖区间 s , t 不是查询的区间 l , r !!!!
return max(ask(i*2,s,mid,l,r),ask(i*2+1,mid+1,r,l,r));
}
//建完树之后,可能还会有修改的需求,根据 对 a[x] 的修改 ,修改 tree[]
//和建树差不多,只是建树是每次dfs到叶子节点就更新对应的 tree[i]
//而修改则是dfs到需要修改的 a[x] 进行更新,对两边dfs完后也是用原来的push求当前区间的tree[i],大差不差
//同样,进行修改要看具体要求的是什么值
void modify(int i,int l,int r,int x,int val)
{
if(x<l||r>x) return;
if(l==x&&r==x)
{
tree[i]=a[x]=val;
return;
}
//如果使用懒惰标签
//if(l<=x&&x<=r)
//{
// tree[i]+=val;
//}
//spread(int i,int l,int r);
int mid=(l+r)>>1;
modify(i*2,l,mid,x,val),modify(i*2+1,mid+1,r,x,val);
push(i);
}
例题:
封印宝石
看题解抄了三天才勉强理解一点
首先,找的宝石一定是 res[i] 的后面的宝石
最大化字典序就注定了要贪心地找 i 之后的最大魔力值的宝石
但是还有个点,每次都要扣除 根据 id 来计算的体力,所以只存魔力值不行,还要存 id
所以用结构体数组 E( id ,ener )
struct E{
int id,ene;
E(){id=ene=0;}
}zero;
同时为了减少体力消耗,同 ener 的情况下,视 id 小的为大
bool operator <(E a,E b)
{
if(a.ene==b.ene) return a.id>b.id;
return a.ene<b.ene;
}
bool operator ==(E a,E b)
{
return a.ene==b.ene;
}
用线段树维护最大 E
但是有个问题,相邻的 res[i] 不能放相同魔力值的宝石,能直接跳过吗?
不能,既然当前最大的魔力值不能放,那就放当前的次大值
也就是说,线段是要求同时维护最大 E 和次大 E
怎么做到呢?
那线段树也开 结构体!
同时存 辖区内的 最大 E 和次大 E
struct Tree
{
int l,r;
E max1,max2;
Tree(){max1.ene=max1.id=0,max2.ene=max2.id=0;};
}tree[4*N];
E al[4];
那么就思考 各个函数该怎么变化
首先是push,我们要对比两个 儿子 tree 来找出最大和次大,再更新到 tree[i]
inline void push(int i)
{
al[0]=tree[i*2].max1,al[1]=tree[i*2].max2;
al[2]=tree[i*2+1].max1,al[3]=tree[i*2+1].max2;
sort(al,al+4);
tree[i].max1=al[3];
for(int j=2;j>=0;j--)
{
if(al[j].ene!=al[j+1].ene)
{
tree[i].max2=al[j];
break;
}
}
}
然后是 build
build 的逻辑就是dfs到每个叶子节点,更新对应的 tree 后return
对之后根据两半的结果更新父节点
所以只要改 跟新tree的操作
inline void build(int i,int l,int r)
{
if(l==r)
{
tree[i].max1.ene=a[l],tree[i].max1.id=l,tree[i].l=l, tree[i].r=r;
return;
}
int mid=(l+r)>>1;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
push(i);
}
然后是 ask
ask 的逻辑就是:我们要查 l ~ r 的数据,
而线段树把区间分成了 2^k 的分段,而任何数都能用 2 进制表示
所以就是从 根节点 1 开始往下dfs,找到在辖区 l ~ r 内的tree [] 的就把这个 tree[] 的数据return 回去
但是现在要找的是最大值和次大值,两个值,一个return无法满足需求
同时,对于查询区间内的所有子段来说,就算return 能返回 最大和次大,也还需要找出总的最大和次大,这不是max函数能做到的,所以要另找两个变量储存当前的 最大和次大,每次找到区间内的子段就找到这两个变量和这个字段的 最大和次大 中的最大和次大,并更新 这两个变量
E m1,m2;
inline E ask(int i,int s,int t,int l,int r)
{
if(l<=s&&t<=r)
{
al[0]=tree[i].max1,al[1]=tree[i].max2;
al[2]=m1,al[3]=m2;
sort(al,al+4);
m1=al[3];
for(int j=2;j>=0;j--)
{
if(al[j].ene!=al[j+1].ene)
{
m2=al[j];
break;
}
}
return tree[i].max1;
}
if(l>t||r<s) return zero;
int mid=(s+t)>>1;
return max(ask(i*2,s,mid,l,r),ask(i*2+1,mid+1,t,l,r));
}
最后是修改函数 modify
我们唯一要做的修改就是把已经拿过的宝石变为0,这样找最大值时就不会找上他,所以不需要更改原数据,只要该 tree 就行
inline void modify(int i,int l,int r,int x)
{
if(x<l||x>r) return;
if(l==x&&r==x)
{
tree[i].max1=tree[i].max2=zero;
return;
}
int mid=(l+r)>>1;
modify(i*2,l,mid,x),modify(i*2+1,mid+1,r,x);
push(i);
}
搞了半天发现最上面的有些狗屁不通,自己都看不懂,虽然本来就是用来给自己看的,下面这个更全面些
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5+10;
LL n,mod,t[4*N],a[N],sum,q;
struct Tag{
LL time,plu;
Tag():time(1),plu(0){};
Tag(LL t,LL p):time(t),plu(p){};
}tag[4*N];
inline void push(LL i)
{
t[i]=(t[i*2]+t[i*2+1])%mod;
}
inline void spread(LL i,LL S,LL T)
{
if(tag[i].time!=1||tag[i].plu!=0)
{
int mid=(S+T)>>1;
tag[i*2].time=(tag[i*2].time*tag[i].time)%mod;
tag[i*2].plu=(tag[i*2].plu*tag[i].time+tag[i].plu)%mod;
t[i*2]=(t[i*2]*tag[i].time+tag[i].plu*(mid-S+1))%mod;
tag[i*2+1].time=(tag[i*2+1].time*tag[i].time)%mod;
tag[i*2+1].plu=(tag[i*2+1].plu*tag[i].time+tag[i].plu)%mod;
t[i*2+1]=(t[i*2+1]*tag[i].time+tag[i].plu*(T-mid))%mod;
tag[i]=Tag(1,0);
}
}
inline void build(LL i,LL l,LL r)
{
if(l==r)
{
t[i]=a[l]%mod;
return;
}
LL mid=(l+r)>>1;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
push(i);
}
inline void modify_t(LL i,LL S,LL T,LL x,LL y,LL k)
{
if(y<S||x>T) return;
if(x<=S&&T<=y)
{
t[i]=(t[i]*k)%mod;
tag[i].time=(tag[i].time*k)%mod;
tag[i].plu=(tag[i].plu*k)%mod;
return;
}
spread(i,S,T);
LL mid=(S+T)>>1;
modify_t(i*2,S,mid,x,y,k);
modify_t(i*2+1,mid+1,T,x,y,k);
push(i);
return;
}
inline void modify_p(LL i,LL S,LL T,LL x,LL y,LL k)
{
if(y<S||x>T) return;
if(x<=S&&T<=y)
{
t[i]=(t[i]+(T-S+1)*k)%mod;
tag[i].plu=(tag[i].plu+k)%mod;
return;
}
spread(i,S,T);
LL mid=(S+T)>>1;
modify_p(i*2,S,mid,x,y,k);
modify_p(i*2+1,mid+1,T,x,y,k);
push(i);
return;
}
inline LL ask(LL i,LL S,LL T,LL x,LL y)
{
if(y<S||x>T) return 0;
if(x<=S&&T<=y)
{
return t[i];
}
spread(i,S,T);
LL mid=(S+T)>>1;
return (ask(i*2,S,mid,x,y)%mod+ask(i*2+1,mid+1,T,x,y)%mod)%mod;
}
int main() {
cin>>n>>q>>mod;
for(LL i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
for(LL i=1;i<=q;i++)
{
LL o,x,y,k;
scanf("%lld",&o);
if(o==1)
{
scanf("%lld%lld%lld",&x,&y,&k);
modify_t(1,1,n,x,y,k);
}
else if(o==2)
{
scanf("%lld%lld%lld",&x,&y,&k);
modify_p(1,1,n,x,y,k);
}
else if(o==3)
{
scanf("%lld%lld",&x,&y);
sum=ask(1,1,n,x,y)%mod;
cout<<sum<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号