最近练了洛谷的三道入门线段树题目
算是熟练了下模板,理解了其思维内涵了
首先是看的教程:
https://www.cnblogs.com/AC-King/p/7789013.html 这个很全面,涵盖了主席树了
https://www.luogu.org/blog/khong-biet/Introduction-of-zkwSegmentTree 很好的非递归线段树,用zkw数就看这个
https://www.luogu.org/blog/pks-LOVING/senior-data-structure-qian-tan-xian-duan-shu-segment-tree 讲的比较本质,但不是很通俗易懂的递归线段树
个人理解:
线段树在不进行区间修改前的部分还是很好理解的,需要注意递归式线段树,先往下处理过程,基本每个函数最后需要通过子树的值维护当前节点的值,个人比较喜欢用struct包装树的节点,保存每个节点维护区间的左右端点的信息。
题目和模板:https://www.luogu.org/problem/P1972
正常递归写法
,由于数据被加强,有两组数据会TLE
这道题也挺有意思的:需要离线做:
思路大致如上,比如4 5 6 3 4 5这段数
其对应线段树叶子的值就是0 0 1 1 1 1,同一类数字,只有最右边的那个值为1,其余为0,这样区间和就可以维护区间种类数了。离线排序做,就O(nlogn)复杂度而已。
#include<bits/stdc++.h>
#define LL long long
#define ms0(x) memset(x,0,sizeof(x))
#define ms-1(x) memset(x,-1,sizeof(x))
using namespace std;
const int maxn = 1e6;
const int iinf=0x3f3f3f3f;
int a[maxn]={
0};//线段树维护的数组
struct node
{
int l,r; //包装了每个节点所维护的对应区间
LL sum; //线段树要维护的值(最大值,和等)
}tr[maxn<<2];node nx,ny;
node build(int i,int l,int r)//这里的i只的是对应存储线段树的数组
{
tr[i].l=l;
tr[i].r=r;
if(l==r)
{
tr[i].sum=a[l];
return tr[i];
}
int mid=(l+r)/2;
nx=build(i<<1,l,mid);
ny=build(i<<1|1,mid+1,r);
tr[i].sum=nx.sum+ny.sum;
return tr[i];
}
int update(int i,int & pos,const int & val)//单点更新
{
if(pos<tr[i].l||tr[i].r<pos)
return tr[i].sum;
if(tr[i].l==pos&&tr[i].r==pos)
return tr[i].sum+=val;
return tr[i].sum=update(i<<1,pos,val)+update(i<<1^1,pos,val);
}
int query(int i,int l,int r)//注意这里的l,r是询问的区间
{
if(tr[i].l>r||tr[i].r<l)
return 0;
if(l<=tr[i].l&&tr[i].r<=r)
return tr[i].sum;
int x,y;
return query(i<<1,l,r)+query(i<<1|1,l,r);
}
inline int re(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
struct INTERVAL{
int l,r;int id;
INTERVAL(int &_l,int &_r,int &_id):l(_l),r(_r),id(_id)
{
}
INTERVAL(){
}
bool operator<(const INTERVAL & tmp)const{
if(r==tmp.r)
return l<tmp.l;
return r<tmp.r;
}
}inter[maxn];
int num[maxn];int pos[maxn]={
0};int ans[maxn];
int main()
{
int n;
n=re();
for(register int j=1