UOJ295 ZJOI2017 线段树

Problem

UOJ

Solution

问题的关键在于如何定位广义线段树上的一段区间 [ l , r ] [l,r] [l,r] 所对应的节点,可以考虑zkw线段树的方法,先找到 l − 1 l-1 l1 r + 1 r+1 r+1 所对应的节点,并且向上跳直到它们的lca处, l − 1 l-1 l1 祖先的所有右儿子以及 r − 1 r-1 r1 祖先的所有左儿子都是对应的节点。需要注意的是为了提取 l = 1 l=1 l=1 r = n r=n r=n 的区间,我们需要加哨兵节点,只需要在原树的顶上多接4个节点即可。

不难发现我们每次都是找到了原树中一条链的所有右儿子和另一条链的所有左儿子。我们可以把链上连续的左儿子连接起来,得到一棵新树,不妨称为左树,同样地,我们可以得到右树。

我们不加证明地给出两棵树一些性质:

  • 两棵树没有交集
  • 左树上一个节点的父亲是 / 原树上最近的一个是右儿子的祖先 / 的兄弟节点,右树类似
  • 此时左树会以 0 0 0 表示的叶子为根,右树会以 n + 1 n+1 n+1 表示的叶子为根
  • 在定位区间时,我们需要找到 l − 1 l-1 l1 r + 1 r+1 r+1 的左右链的起始位置,讨论一下可以得到:
    • 左儿子的左链起始于左树上的父亲,右链起始于兄弟
    • 右儿子的右链起始于右树上的父亲,左链起始于兄弟

我们用一个虚拟的根把两棵树并在一起,这样每次询问的点集就是新树上的两条路径。注意到答案是可以差分的,把询问拆成两个,询问的点集就必然是一个点到根的路径了。

怎么求一个点到根路径上的点集呢?可以联想到树上莫队的方法,用dfs序记录一个节点的进出,那么 x x x 进入时的前缀就表示了这个点集。现在问题变成了:向关键点集中加入/删除一个元素,询问一个点到关键点集的树上距离和。

把距离表达式拆开,然后用树剖+树状数组即可做到 O ( ( n + m ) log ⁡ 2 n ) O((n+m)\log ^2 n) O((n+m)log2n)

Code

实现的体验不怎么好,还好细节不太多,没调太久。刚开始打完了树剖发现只写了3.1k,感觉快写完了,还以为自己写得好短,结果后来发现忘了写倍增,原树的一些信息没有初始化……不知不觉就4.4k了

写之前没好好构思排版,写得过程中处理边界情况有点难受,担心思路混乱就代码写得丑了点,现在写完不想改了。。所以就这么长吧,好像是今年写过最长的题了

#include 
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=400010;
template <typename Tp> int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> void read(Tp &x)
{
	x=0;char ch=getchar();int f=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=1,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();
	if(f) x=-x;
}
int n,m,rt,tot,qc,now,seq[maxn<<1],dfn[maxn],dep[maxn];
int mid[maxn],lc[maxn],rc[maxn],nxt[maxn],L[maxn],R[maxn],lst[maxn],rst[maxn];
ll sd,ans[maxn];
struct Query{
	int pos,x,f,id;
	bool operator < (const Query &b)const{return pos<b.pos;}
}q[maxn<<1];
namespace Tree{
	struct data{int v,nxt;}edge[maxn];
	int tot,p,head[maxn],f[18][maxn];
	void insert(int u,int v){edge[++p]=(data){v,head[u]};head[u]=p;}
	int in(int a,int b,int c,int d){return a<=c&&d<=b;}//[c,d] in [a,b]?
	void dfs(int x)
	{
		seq[++tot]=x;dfn[x]=tot;
		for(int i=1;i<18;i++) f[i][x]=f[i-1][f[i-1][x]];
		for(int i=head[x];i;i=edge[i].nxt)
		{
			f[0][edge[i].v]=x;
			dfs(edge[i].v);
		}
		seq[++tot]=-x;
	}
	int ql(int x,int l,int r)
	{
		if(!in(l,r,L[x],R[x])) return x;
		for(int i=17;~i;i--)
		  if(f[i][x]&&in(l,r,L[f[i][x]],R[f[i][x]]))
		    x=f[i][x];
		return f[0][x];
	}
	int qr(int x,int l,int r)
	{
		if(!in(l,r,L[x],R[x])) return x;
		for(int i=17;~i;i--)
		  if(f[i][x]&&in(l,r,L[f[i][x]],R[f[i][x]]))
		    x=f[i][x];
		return f[0][x];
	}
}
using Tree::insert;
namespace TCL{
	int dfc,f[maxn],sz[maxn],hs[maxn],top[maxn],pos[maxn];
	struct BIT{
		ll a[maxn],a2[maxn];
		void add(int p,int v)
		{
			for(int i=p;i<=tot;i+=lowbit(i)) a[i]+=v,a2[i]+=(ll)p*v;
		}
		ll qsum(int p)
		{
			ll res=0ll;
		    for(int i=p;i;i-=lowbit(i)) res+=(ll)(p+1)*a[i]-a2[i];
		    return res;
		}
	}T;
	void dfs1(int x)
	{
		sz[x]=1;
		if(!lc[x]) return ;
		f[lc[x]]=f[rc[x]]=x;
		dfs1(lc[x]);sz[x]+=sz[lc[x]];
		dfs1(rc[x]);sz[x]+=sz[rc[x]];
		hs[x]=sz[lc[x]]<sz[rc[x]]?rc[x]:lc[x];
	}
	void dfs2(int x,int s)
	{
		top[x]=s;pos[x]=++dfc;
		if(!lc[x]) return ;
		if(hs[x]) dfs2(hs[x],s);
		if(lc[x]^hs[x]) dfs2(lc[x],lc[x]);
		if(rc[x]^hs[x]) dfs2(rc[x],rc[x]);
	}
	void update(int x,int v)
	{
		for(int fx=top[x];x;x=f[fx],fx=top[x])
		  T.add(pos[fx],v),T.add(pos[x]+1,-v);
	}
	ll query(int x)
	{
		ll res=0ll;
		for(int fx=top[x];x;x=f[fx],fx=top[x])
		  res+=T.qsum(pos[x])-T.qsum(pos[fx]-1);
		return res;
	}
}
using TCL::update;
using TCL::query;
void build(int l,int r,int &rt)
{
	rt=++tot;L[rt]=l;R[rt]=r;
	if(l==r) return ;
	read(mid[rt]);
	build(l,mid[rt],lc[rt]);
	build(mid[rt]+1,r,rc[rt]);
	nxt[lc[rt]]=rc[rt];nxt[rc[rt]]=lc[rt];
}
void dfs(int x,int ls,int rs,int l)//ls stands for the nearest anc which is leftson
{
	if(x<(n<<1)) l?insert(nxt[rs],x):insert(nxt[ls],x);
	if(!lc[x])
	{
		if(L[x]==0){lst[L[x]]=rst[L[x]]=x;return ;}
		if(L[x]==n+1){lst[L[x]]=1;rst[L[x]]=x;return ;}
		if(l){lst[L[x]]=nxt[rs];rst[L[x]]=nxt[x];}
		else{lst[L[x]]=nxt[x];rst[L[x]]=nxt[ls];}
		return ;
	}
	dep[lc[x]]=dep[rc[x]]=dep[x]+1;
	if(l)
	{
		dfs(lc[x],x,rs,1);
		dfs(rc[x],x,rs,0);
	}
	else
	{
		dfs(lc[x],ls,x,1);
		dfs(rc[x],ls,x,0);
	}
}
void input()
{
	int x,l,r,lx,lf,rx,rf;
	read(n);build(1,n,rt);
	//deal with extra nodes initial information
	lc[tot+2]=tot+1;rc[tot+2]=tot+3;
	lc[tot+3]=rt;rc[tot+3]=tot+4;
	L[tot+1]=R[tot+1]=0;
	L[tot+2]=0;R[tot+2]=n+1;
	L[tot+3]=1;R[tot+3]=n+1;
	L[tot+4]=R[tot+4]=n+1;
	nxt[tot+1]=tot+3;nxt[tot+3]=tot+1;
	nxt[1]=tot+4;nxt[tot+4]=1;
	insert(0,tot+1);insert(0,tot+4);
	rt=tot+2;tot+=4;dep[rt]=1;
	//build another tree and use its DFN to get ans
	dfs(rt,0,0,1);
	Tree::dfs(0);
	TCL::dfs1(rt);
	TCL::dfs2(rt,rt);
	//divide query into four parts 
	read(m);
	for(int i=1;i<=m;i++)
	{
		read(x);read(l);read(r);
		lx=rst[l-1];rx=lst[r+1];
		lf=Tree::ql(lx,l,r);
		rf=Tree::qr(rx,l,r);
		q[++qc]=(Query){dfn[lx],x,1,i};
		q[++qc]=(Query){dfn[lf],x,-1,i};
		q[++qc]=(Query){dfn[rx],x,1,i};
		q[++qc]=(Query){dfn[rf],x,-1,i};
	}
	sort(q+1,q+qc+1);
}
int main()
{
	input();
	for(int i=1,j=1;i<=Tree::tot;i++)
	{
		if(seq[i])
		{
			update(abs(seq[i]),seq[i]>0?1:-1);
			seq[i]>0?++now:--now;
			seq[i]>0?sd+=dep[seq[i]]:sd-=dep[-seq[i]];
		}
		for(;j<=qc&&q[j].pos<=i;j++)
		  ans[q[j].id]+=q[j].f*(sd+(ll)now*dep[q[j].x]-2ll*query(q[j].x));
	}
	for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
	return 0;
}

你可能感兴趣的:(线段树,好题集,UOJ)