吉老师线段树
感觉就是普通的线段树+暴力
常见的套路有区间取模,区间开根等
因为我做题少就见过这俩
你也看见题目上的Ⅰ了,碰到再更新
所以为什么叫吉老师线段树,为什么不叫老头子线段树
于是我去学习了一下历史
上两道今天写的题:
1.区间开根+区间求和
传送门
因为一个数字开log次就变成1了,于是我们可以暴力修改开根,保存一个区间的maxx,如果这个数为1了那么就不用开了,预计时间复杂度是Nlog的
代码如下:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int n,m,a[N];
struct Segtree{
int l,r,sum,maxx;
}tree[N<<2];
inline void push_up(int root){
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
tree[root].maxx=max(tree[root<<1].maxx,tree[root<<1|1].maxx);
}
void build(int root,int l,int r){
tree[root].l=l;
tree[root].r=r;
if(l==r){
tree[root].sum=tree[root].maxx=a[l];
return ;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
push_up(root);
}
void modify(int root,int l,int r,int L,int R){
if(l==r){
tree[root].maxx=sqrt(tree[root].maxx);
tree[root].sum=sqrt(tree[root].sum);
return ;
}
if(tree[root].maxx==1) return ;
int mid=(l+r)>>1;
if(L<=mid&&tree[root<<1].maxx>1) modify(root<<1,l,mid,L,R);
if(R>mid&&tree[root<<1|1].maxx>1) modify(root<<1|1,mid+1,r,L,R);
push_up(root);
}
int query(int root,int l,int r,int L,int R){
if(L<=l&&R>=r){
return tree[root].sum;
}
int ans=0;
int mid=(l+r)>>1;
if(L<=mid) ans+=query(root<<1,l,mid,L,R);
if(R>mid) ans+=query(root<<1|1,mid+1,r,L,R);
return ans;
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=m;i++){
int op,l,r;
scanf("%lld%lld%lld",&op,&l,&r);
if(op==1) modify(1,1,n,l,r);
else printf("%lld\n",query(1,1,n,l,r));
}
return 0;
}
2.区间修改+区间开根+区间求和
传送门
线段树神题.jpg
一开始看到这题,直接拿上面那题加了个普通的加法就交上去了,然后顺利成章的就T了(
因为重复操作1和操作2,那么时间复杂度就变成平方了()
所以开根的方式我们得考虑变得更快一些:
必然是对一串数字进行更新,我们考虑一个区间,如果最大值和最小值开根之后减少的值一样,那么这段区间开根不就可以变成区间减法了,再加上上面那个>1的判断,就可以以比nlog稍微多一点的时间复杂度跑过去。
代码如下
#include <bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
const int N=200005;
int n,m,a[N];
struct NODE{
int l,r,lazy,sum,maxx,minn;
}tree[N<<2];
void push_up(int root){
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
tree[root].maxx=max(tree[root<<1].maxx,tree[root<<1|1].maxx);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}
void push_down(int root){
if(tree[root].lazy!=0){
tree[root<<1].lazy+=tree[root].lazy;
tree[root<<1|1].lazy+=tree[root].lazy;
tree[root<<1].maxx+=tree[root].lazy;
tree[root<<1|1].maxx+=tree[root].lazy;
tree[root<<1].minn+=tree[root].lazy;
tree[root<<1|1].minn+=tree[root].lazy;
tree[root<<1].sum+=(tree[root<<1].r-tree[root<<1].l+1)*tree[root].lazy;
tree[root<<1|1].sum+=(tree[root<<1|1].r-tree[root<<1|1].l+1)*tree[root].lazy;
tree[root].lazy=0;
push_up(root);
}
return ;
}
void build(int root,int l,int r){
tree[root].l=l;
tree[root].r=r;
if(l==r){
tree[root].minn=tree[root].maxx=tree[root].sum=a[l];
return ;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
push_up(root);
}
void modify(int root,int l,int r,int x){
tree[root].lazy+=x;
tree[root].sum+=(r-l+1)*x;
tree[root].maxx+=x;
tree[root].minn+=x;
}
void modify2(int root,int l,int r,int L,int R){
if(L<=l&&R>=r){
int cha1=tree[root].minn-sqrt(tree[root].minn);
int cha2=tree[root].maxx-sqrt(tree[root].maxx);
if(cha1==cha2){//我把区间开根变成区间减法
modify(root,l,r,-1*cha1);
return ;
}
}
push_down(root);
int mid=(l+r)>>1;
if(L<=mid) modify2(root<<1,l,mid,L,R);
if(mid<R) modify2(root<<1|1,mid+1,r,L,R);
push_up(root);
}
void modify2(int root,int l,int r,int L,int R,int x){
if(L<=l&&R>=r){
tree[root].lazy+=x;
tree[root].maxx+=x;
tree[root].minn+=x;
tree[root].sum+=(r-l+1)*x;
return ;
}
push_down(root);
int mid=(l+r)>>1;
if(L<=mid) modify2(root<<1,l,mid,L,R,x);
if(mid<R) modify2(root<<1|1,mid+1,r,L,R,x);
push_up(root);
}
int query(int root,int l,int r,int L,int R){
if(L<=l&&R>=r) return tree[root].sum;
push_down(root);
int ans=0;
int mid=(l+r)>>1;
if(L<=mid) ans+=query(root<<1,l,mid,L,R);
if(mid<R) ans+=query(root<<1|1,mid+1,r,L,R);
return ans;
}
void modify1(int root,int l,int r,int L,int R){
if(L<=l&&R>=r){
int cha1=tree[root].maxx-(ll)sqrt(tree[root].maxx);
int cha2=tree[root].minn-(ll)sqrt(tree[root].minn);
if(cha1==cha2){
modify(root,l,r,-cha1);
return ;
}
}
if(tree[root].maxx==1) return ;
push_down(root);
int mid=(l+r)>>1;
if(L<=mid&&tree[root<<1].maxx>1) modify1(root<<1,l,mid,L,R);
if(mid<R&&tree[root<<1|1].maxx>1) modify1(root<<1|1,mid+1,r,L,R);
push_up(root);
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=m;i++){
int op,l,r,x;
scanf("%lld%lld%lld",&op,&l,&r);
if(op==1)modify1(1,1,n,l,r);
if(op==2){
scanf("%lld",&x);
modify2(1,1,n,l,r,x);
}
else if(op==3) printf("%lld\n",query(1,1,n,l,r));
}
return 0;
}