[题解]P1505 [国家集训队]旅游

洛谷P1505

看到树上点对之间的修改和询问就知道是树剖了。

由于询问和操作都是在边上,我们还需要将边权转为点权。与其他树剖题不同的是,C i操作需要我们直接修改一条指定的边的边权,而不是修改两点之间的边权,所以我们还需要记录每条边将边权转到了哪个点上。

具体操作方法:

il void dfs1(int x,int fa)
{
	siz[x]=1;son[x]=0;dep[x]=dep[fa]+1;f[x]=fa;
	for(re int i=h[x];i;i=e[i].nxt)
		if(e[i].v^fa)
		{
			etp[(i+1)>>1]=e[i].v;
			W[e[i].v]=e[i].w;
			dfs1(e[i].v,x);
			siz[x]+=siz[e[i].v];
			son[x]=siz[son[x]]<siz[e[i].v]?e[i].v:son[x];
		}
}

我们可以在第一次\(\text{dfs}\)中存下每条边指向的儿子的编号,也就代码中的ept[]\(edge\ to\ point\))。

解决了边转点的问题后,我们再来考虑如何用线段树来维护题目要求的操作。

  1. C i 就是在\(dfn[etp[i]]\)上单点修改

    il void change1(int p,int pos,int x)
    {
    	if(L[p]==R[p]){Sum[p]=Max[p]=Min[p]=x;return;}
    	re int mid=(L[p]+R[p])>>1;
    	pushdown(p);
    	if(pos>mid) change1(rs(p),pos,x);
    	else change1(ls(p),pos,x);
    	pushup(p);
    }
    
    il void modify1(int p,int w){change1(1,dfn[p],w);}
    
    if(ch=='C') modify1(etp[u],v);
    
  2. N u v区间取反,也就是区间乘以\(-1\)(注意最大值和最小值要交换)

    il void pushdown(int p)
    {
    	if(!lz[p]) return;
    	Sum[ls(p)]*=-1;Sum[rs(p)]*=-1;
    	swap(Max[ls(p)],Min[ls(p)]);
    	swap(Max[rs(p)],Min[rs(p)]);
    	Max[ls(p)]*=-1;Min[ls(p)]*=-1;
    	Max[rs(p)]*=-1;Min[rs(p)]*=-1;
    	lz[ls(p)]^=1;lz[rs(p)]^=1;
    	lz[p]=0;
    }
    il void change2(int p,int l,int r)
    {
    	if(L[p]>r||R[p]<l) return;
    	if(L[p]>=l&&R[p]<=r)
    	{
    		lz[p]^=1;
    		Sum[p]*=-1;
    		swap(Max[p],Min[p]);
    		Max[p]*=-1;Min[p]*=-1;
    		return;
    	}
    	pushdown(p);
    	change2(ls(p),l,r);
    	change2(rs(p),l,r);
    	pushup(p);
    }
    
    il void modify2(int u,int v)
    {
    	while(top[u]^top[v])
    	{
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		change2(1,dfn[top[u]],dfn[u]);
    		u=f[top[u]];
    	}
    	if(dep[u]<dep[v]) swap(u,v);
    	change2(1,dfn[v]+1,dfn[u]);
    }
    
    if(ch=='N') modify2(u+1,v+1);
    
  3. SUM u v也就是区间求和

    il int query1(int p,int l,int r)
    {
    	if(L[p]>r||R[p]<l) return 0;
    	if(L[p]>=l&&R[p]<=r) return Sum[p];
    	pushdown(p);
    	return query1(ls(p),l,r)+query1(rs(p),l,r);
    }
    
    il int ask1(int u,int v)
    {
    	re int res=0;
    	while(top[u]^top[v])
    	{
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		res+=query1(1,dfn[top[u]],dfn[u]);
    		u=f[top[u]];
    	}
    	if(dep[u]<dep[v]) swap(u,v);
    	res+=query1(1,dfn[v]+1,dfn[u]);
    	return res;
    }
    
    if(ch=='S') print(ask1(u+1,v+1));
    
  4. MAX u vMIN u v区间最值查询

    il int query2(int p,int l,int r)
    {
    	if(L[p]>r||R[p]<l) return -inf;
    	if(L[p]>=l&&R[p]<=r) return Max[p];
    	pushdown(p);
    	return max(query2(ls(p),l,r),query2(rs(p),l,r));
    }
    il int query3(int p,int l,int r)
    {
    	if(L[p]>r||R[p]<l) return inf;
    	if(L[p]>=l&&R[p]<=r) return Min[p];
    	pushdown(p);
    	return min(query3(ls(p),l,r),query3(rs(p),l,r));
    }
    
    
    il int ask2(int u,int v)
    {
    	re int res=-inf;
    	while(top[u]^top[v])
    	{
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		res=max(res,query2(1,dfn[top[u]],dfn[u]));
    		u=f[top[u]];
    	}
    	if(dep[u]<dep[v]) swap(u,v);
    	res=max(res,query2(1,dfn[v]+1,dfn[u]));
    	return res;
    }
    il int ask3(int u,int v)
    {
    	re int res=inf;
    	while(top[u]^top[v])
    	{
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		res=min(res,query3(1,dfn[top[u]],dfn[u]));
    		u=f[top[u]];
    	}
    	if(dep[u]<dep[v]) swap(u,v);
    	res=min(res,query3(1,dfn[v]+1,dfn[u]));
    	return res;
    }
    
    if(ch=='A') print(ask2(u+1,v+1));
    if(ch=='I') print(ask3(u+1,v+1));
    

完整代码太长,就不给了。

posted @ 2020-12-04 16:27  watermonster1y1  阅读(31)  评论(0编辑  收藏  举报