线段树

(远古代码了,还在用指针)

线段树

P3372 【模板】线段树 1

P3373 【模板】线段树 2

注意 \(pushdown\) 操作的优先级:

儿子的值 \(=\) 此刻儿子的值 \(\times\) 爸爸的乘法 \(lazy~+\) 儿子的区间长度 \(\times\) 爸爸的加法 \(lazy\)

即:先下传 \(lazymul\) ,再下传 \(lazyadd\)

$\texttt{code}$
struct Tree
{
	 int sum,add,mul;
}tree[Maxn*4];
void pushdown(int p,int nl,int nr)
{
	 if(nl==nr) return;
	 int mid=(nl+nr)>>1,tmpm=tree[p].mul,tmpa=tree[p].add,ls=p<<1,rs=p<<1|1;
	 tree[ls].add=1ll*tree[ls].add*tmpm%mod,tree[rs].add=1ll*tree[rs].add*tmpm%mod;
	 tree[ls].mul=1ll*tree[ls].mul*tmpm%mod,tree[rs].mul=1ll*tree[rs].mul*tmpm%mod;
	 tree[ls].sum=1ll*tree[ls].sum*tmpm%mod,tree[rs].sum=1ll*tree[rs].sum*tmpm%mod;
	 tree[ls].sum=(tree[ls].sum+1ll*(mid-nl+1ll)*tmpa)%mod;
	 tree[rs].sum=(tree[rs].sum+1ll*(nr-mid)*tmpa)%mod;
	 tree[ls].add=(tree[ls].add+tmpa)%mod,tree[rs].add=(tree[rs].add+tmpa)%mod;
	 tree[p].add=0,tree[p].mul=1;
}
void add(int p,int nl,int nr,int l,int r,int k)
{
	 if(nl>=l && nr<=r)
	 {
	 	 tree[p].sum=(tree[p].sum+1ll*k*(nr-nl+1ll))%mod;
	 	 tree[p].add=(tree[p].add+k)%mod;
	 	 return;
	 }
	 pushdown(p,nl,nr);
	 int mid=(nl+nr)>>1;
	 if(mid>=l) add(p<<1,nl,mid,l,r,k);
	 if(mid<r) add(p<<1|1,mid+1,nr,l,r,k);
	 tree[p].sum=(tree[p<<1].sum+tree[p<<1|1].sum)%mod;
}
void mul(int p,int nl,int nr,int l,int r,int k)
{
	 if(nl>=l && nr<=r)
	 {
	 	 tree[p].mul=1ll*tree[p].mul*k%mod;
	 	 tree[p].add=1ll*tree[p].add*k%mod;
	 	 tree[p].sum=1ll*tree[p].sum*k%mod;
	 	 return;
	 }
	 pushdown(p,nl,nr);
	 int mid=(nl+nr)>>1;
	 if(mid>=l) mul(p<<1,nl,mid,l,r,k);
	 if(mid<r) mul(p<<1|1,mid+1,nr,l,r,k);
	 tree[p].sum=(tree[p<<1].sum+tree[p<<1|1].sum)%mod;
}

线段树常见玩法:

扫描线

线段树的应用 。

关于扫描线为什么不用 \(pushdown\) 操作:

这题如果一个结点在 覆盖这个区间时下传了标记 ,整棵子树都被覆盖一次,取消覆盖时要遍历整棵子树重新获取信息

$\texttt{solution}$

大致步骤:

  • 先进行离散化处理 。

  • 处理矩形的上下边界,变为一条条扫描线 。

  • 进行区间加操作 。

每个区间( 线段树上的点 )记录信息:

  • 有多少条线段 直接 将这条线段全部覆盖( 从父亲那里继承来的不能算 ) 。

  • 这个区间内被矩形覆盖到的实际宽度之和 。

注意:

  • 进行区间加操作时的下标是离散化后的下标,而统计的答案是实际大小 。

  • 空间限制宽泛,当算不清楚到底要开多大时尽量往打了开 。

大致过程如图所示:

$\texttt{code}$
#include<bits/stdc++.h>
using namespace std;
#define Maxn 200005
typedef long long ll;
ll n,tot,l,r,ans,cnt;
ll X[Maxn][2],Y[Maxn][2],True[Maxn*2];
struct Dis { ll num,val; } tmp[Maxn*2];
struct Seg { ll xl,xr,h,val; } seg[Maxn*2];
struct Tree
{
	 ll sum;
	 ll Lazy;
}tree[Maxn*8];
bool cmp1(Dis x,Dis y) { return x.val<y.val; }
bool cmp2(Seg x,Seg y) { return x.h<y.h; }
void pushup(ll p,ll nl,ll nr)
{
	 if(tree[p].Lazy) tree[p].sum=True[nr+1]-True[nl];
	 else tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
}
void add(ll p,ll nl,ll nr,ll k)
{
	 if(nl>=l && nr<=r)
	 {
	 	 tree[p].Lazy+=k;
	 	 pushup(p,nl,nr);
	 	 return;
	 }
	 ll mid=(nl+nr)>>1;
	 if(mid>=l) add(p<<1,nl,mid,k);
	 if(mid<r) add(p<<1|1,mid+1,nr,k);
	 pushup(p,nl,nr);
}
int main()
{
     //freopen(".in","r",stdin);
     //freopen(".out","w",stdout);
	 scanf("%lld",&n);
	 for(ll i=1;i<=n;i++) scanf("%lld%lld%lld%lld",&X[i][0],&Y[i][0],&X[i][1],&Y[i][1]);
	 for(ll i=1;i<=n;i++) tmp[i*2-1]=(Dis){i*2-1,X[i][1]},tmp[i*2]=(Dis){i*2,X[i][0]};
	 sort(tmp+1,tmp+n*2+1,cmp1);
	 tmp[0].val=-1;
	 for(ll i=1;i<=n*2;i++)
	 {
	 	 if(tmp[i].val!=tmp[i-1].val) True[++cnt]=tmp[i].val;
		 X[(tmp[i].num+1)/2][tmp[i].num%2]=cnt;
	 } // 离散化 
	 for(ll i=1;i<=n;i++)
	 	 seg[++tot]=(Seg){X[i][0],X[i][1],Y[i][0],1},
	 	 seg[++tot]=(Seg){X[i][0],X[i][1],Y[i][1],-1};
	 sort(seg+1,seg+tot+1,cmp2); // 以上是预处理部分 
	 for(ll i=1;i<=tot;i++)
	 {
	 	 ans=ans+1ll*(seg[i].h-seg[i-1].h)*tree[1].sum;
	 	 l=seg[i].xl,r=seg[i].xr-1;
	 	 add(1,1,cnt,seg[i].val);
	 }
	 printf("%lld\n",ans);
     //fclose(stdin);
     //fclose(stdout);
     return 0;
}
$\texttt{solution}$

其他部分与扫描线模板一样,就是统计的时候记录这个区间被分为多少个不相交的线段,另外进行两次扫描线( 横着一次,竖着一次 ) 。

如何记录被分为多少个线段:

  • 记录的值:( 一条线段 \([l,r]\) 实际表示 \([True_l,True_{r+1}-1]\) ,这同扫描线模板一样 )

    • \(tag\) : 整个区间被整体覆盖了几次( 不下传 )

    • \(sum\) : 整个区间被几条互不相交的线段覆盖

    • \(Coverl\) : 左端点是否被覆盖( 合并用 )

    • \(Coverr\) : 右端点是否被覆盖( 合并用 )

  • \(pushup\) 操作

$\texttt{code}$
#include<bits/stdc++.h>
using namespace std;
#define infll 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define Maxn 40005
typedef long long ll; // 习惯性开大空间 
ll maxll(ll x,ll y){ return x>y?x:y; }
ll minll(ll x,ll y){ return x<y?x:y; }
int n,l,r,cntx,cnty,tot;
int Truex[Maxn*2],Truey[Maxn*2],X[Maxn][2],Y[Maxn][2];
ll ans=0;
struct Dis { int num,val; } Inx[Maxn*2],Iny[Maxn*2];
struct Seg { int nl,nr,h,val; } segx[Maxn*2],segy[Maxn*2];
bool cmp1(Dis x,Dis y){ return x.val<y.val; }
bool cmp2(Seg x,Seg y){ return x.h<y.h; }
struct Tree
{
	 int sum,tag,Coverl,Coverr;
}tree[Maxn*16];
void workpre()
{
	 scanf("%d",&n),tot=n*2;
	 for(int i=1;i<=n;i++) scanf("%d%d%d%d",&X[i][0],&Y[i][0],&X[i][1],&Y[i][1]);
	 for(int i=1;i<=n;i++) Inx[i*2-1]=(Dis){i*2-1,X[i][1]},Inx[i*2]=(Dis){i*2,X[i][0]};
	 for(int i=1;i<=n;i++) Iny[i*2-1]=(Dis){i*2-1,Y[i][1]},Iny[i*2]=(Dis){i*2,Y[i][0]};
	 sort(Inx+1,Inx+tot+1,cmp1),sort(Iny+1,Iny+tot+1,cmp1);
	 Inx[0].val=Iny[0].val=-inf;
	 for(int i=1;i<=tot;i++)
	 {
	 	 if(Inx[i].val!=Inx[i-1].val) Truex[++cntx]=Inx[i].val;
	 	 if(Iny[i].val!=Iny[i-1].val) Truey[++cnty]=Iny[i].val;
	 	 X[(Inx[i].num+1)/2][Inx[i].num%2]=cntx;
	 	 Y[(Iny[i].num+1)/2][Iny[i].num%2]=cnty;
	 }
	 for(int i=1;i<=n;i++)
	 {
	 	 segx[i*2-1]=(Seg){X[i][0],X[i][1],Y[i][0],1};
	 	 segx[i*2]=(Seg){X[i][0],X[i][1],Y[i][1],-1};
	 	 segy[i*2-1]=(Seg){Y[i][0],Y[i][1],X[i][0],1};
	 	 segy[i*2]=(Seg){Y[i][0],Y[i][1],X[i][1],-1};
	 }
	 sort(segx+1,segx+tot+1,cmp2),sort(segy+1,segy+tot+1,cmp2);
}
void build(int p,int nl,int nr)
{
	 tree[p].sum=tree[p].tag=tree[p].Coverl=tree[p].Coverr=0;
	 if(nl==nr) return;
	 int mid=(nl+nr)>>1;
	 build(p<<1,nl,mid),build(p<<1|1,mid+1,nr);
}
void pushup(int p,int nl,int nr)
{
	 if(tree[p].tag) tree[p].sum=tree[p].Coverl=tree[p].Coverr=1;
	 else if(nl==nr) tree[p].sum=tree[p].Coverl=tree[p].Coverr=0;
	 else
	 {
	 	 tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
	 	 if(tree[p<<1].Coverr && tree[p<<1|1].Coverl) tree[p].sum--;
	 	 tree[p].Coverl=tree[p<<1].Coverl;
	 	 tree[p].Coverr=tree[p<<1|1].Coverr;
	 }
}
void add(int p,int nl,int nr,int k)
{
	 if(nl>=l && nr<=r)
	 {
	 	 tree[p].tag+=k;
	 	 pushup(p,nl,nr);
	 	 return;
	 }
	 int mid=(nl+nr)>>1;
	 if(mid>=l) add(p<<1,nl,mid,k);
	 if(mid<r) add(p<<1|1,mid+1,nr,k);
	 pushup(p,nl,nr);
}
int main()
{
     //freopen(".in","r",stdin);
     //freopen(".out","w",stdout);
     workpre(); // 输入 + 初始化 
	 for(int i=1;i<tot;i++)
	 {
	 	 l=segx[i].nl,r=segx[i].nr-1;
	 	 add(1,1,cntx,segx[i].val);
	 	 ans+=2ll*tree[1].sum*(Truey[segx[i+1].h]-Truey[segx[i].h]);
	 }
	 build(1,1,cnty);
	 for(int i=1;i<tot;i++)
	 {
	 	 l=segy[i].nl,r=segy[i].nr-1;
	 	 add(1,1,cnty,segy[i].val);
	 	 ans+=2ll*tree[1].sum*(Truex[segy[i+1].h]-Truex[segy[i].h]);
	 }
	 printf("%lld\n",ans);
     //fclose(stdin);
     //fclose(stdout);
     return 0;
}

区间最长的 XX

比如最长的由 \(0\) 组成的序列 。

可以在每一个节点存储:

  • 从区间最左端开始的最长序列

  • 从区间最右端开始的最长序列

  • 整个区间的最长序列

合并时参考代码 。

例题:

P6492 [COCI2010-2011#6] STEP

P2894 [USACO08FEB]Hotel G

struct Tree
{
	 int ml,mr,ans;
}tree[Maxn<<2];
void pushup(int p,int nl,int nr)
{
	 int mid=(nl+nr)>>1;
	 tree[p].ml=tree[p<<1].ml,tree[p].mr=tree[p<<1|1].mr;
	 if(tree[p<<1].ml==mid-nl+1) tree[p].ml=tree[p<<1].ml+tree[p<<1|1].ml;
	 if(tree[p<<1|1].mr==nr-mid) tree[p].mr=tree[p<<1].mr+tree[p<<1|1].mr;
	 tree[p].ans=max(max(tree[p<<1].ans,tree[p<<1|1].ans),tree[p<<1].mr+tree[p<<1|1].ml);
}
void build(int p,int nl,int nr)
{
	 if(nl==nr) { tree[p].ml=tree[p].mr=tree[p].ans=1; return; }
	 int mid=(nl+nr)>>1;
	 if(mid>=nl) build(p<<1,nl,mid);
	 if(mid<nr) build(p<<1|1,mid+1,nr);
	 pushup(p,nl,nr);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值