Luogu P1600 天天爱跑步

Posted cjjsb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P1600 天天爱跑步相关的知识,希望对你有一定的参考价值。

昨天的原题大战考到了这题,发现我之前竟然没做过就顺便水篇博客

首先对于一条路径(x o y),我们显然可以根据它们的(operatorname{LCA})把路径分成两段

  • 对于路径(x o z),我们发现上面的所有点满足时间与深度之和不变
  • 对于路径(z o y),我们发现上面的所有点满足时间与深度之差不变

那么很自然我们想到分别维护两种路径,不过要注意(z)不要算重了

现在就以第一种和的路径为例,我们发现树上路径加增量的操作显然可以直接差分

由于差分之后需要求的是子树和,我们很容易想到把时间与深度之和作为下标建线段树,每次上传直接线段树合并即可

总复杂度(O(nlog n)),就是要注意内存别MLE了

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=300005,SZ=1e7,P=20;
struct edge
{
	int to,nxt;
}e[N<<1]; int n,m,head[N],cnt,x,y,w[N],dep[N],anc[N][P],ans[N],rt1[N],rt2[N];
inline void addedge(CI x,CI y)
{
	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
	e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
}
class Segment_Tree
{
	private:
		struct segment
		{
			int ch[2],sum;
		}node[SZ]; int tot;
	public:
		#define lc(x) node[x].ch[0]
		#define rc(x) node[x].ch[1]
		#define S(x) node[x].sum
		#define TN CI l=0,CI r=3*n
		inline void insert(int& now,CI pos,CI mv,TN)
		{
			if (!now) now=++tot; if (l==r) return (void)(S(now)+=mv); int mid=l+r>>1;
			if (pos<=mid) insert(lc(now),pos,mv,l,mid); else insert(rc(now),pos,mv,mid+1,r);
		}
		inline void merge(int& x,CI y,TN)
		{
			if (!y) return; if (!x) return (void)(x=y); S(x)+=S(y);
			int mid=l+r>>1; merge(lc(x),lc(y),l,mid); merge(rc(x),rc(y),mid+1,r);
		}
		inline int query(CI now,CI pos,TN)
		{
			if (!now) return 0; if (l==r) return S(now); int mid=l+r>>1;
			if (pos<=mid) return query(lc(now),pos,l,mid); else return query(rc(now),pos,mid+1,r);
		}
		#undef lc
		#undef rc
		#undef S
		#undef TN
}SEG1,SEG2;
#define to e[i].to
class Double_Increased
{
	public:
		inline int getlca(int x,int y)
		{
			if (dep[x]<dep[y]) swap(x,y); RI i;
			for (i=P-1;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i];
			if (x==y) return x; for (i=P-1;~i;--i)
			if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
			return anc[x][0];
		}
		inline void DFS1(CI now=1,CI fa=0)
		{
			dep[now]=dep[anc[now][0]=fa]+1; RI i; for (i=0;i<P-1;++i)
			if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break;
			for (i=head[now];i;i=e[i].nxt) if (to!=fa) DFS1(to,now);
		}
		inline void DFS2(CI now=1,CI fa=0)
		{
			for (RI i=head[now];i;i=e[i].nxt) if (to!=fa)
			DFS2(to,now),SEG1.merge(rt1[now],rt1[to]),SEG2.merge(rt2[now],rt2[to]);
			ans[now]=SEG1.query(rt1[now],n+dep[now]+w[now])+SEG2.query(rt2[now],n+dep[now]-w[now]);
		}
}T;
#undef to
int main()
{
	//freopen("race.in","r",stdin); freopen("race.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&m),i=1;i<n;++i)
	scanf("%d%d",&x,&y),addedge(x,y);
	for (i=1;i<=n;++i) scanf("%d",&w[i]);
	for (T.DFS1(),i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y); int z=T.getlca(x,y);
		SEG1.insert(rt1[x],n+dep[x],1); if (z!=1) SEG1.insert(rt1[anc[z][0]],n+dep[x],-1);
		SEG2.insert(rt2[z],n+dep[z]-(dep[x]-dep[z]),-1);
		SEG2.insert(rt2[y],n+dep[z]-(dep[x]-dep[z]),1);
	}
	for (T.DFS2(),i=1;i<=n;++i) printf("%d ",ans[i]); return 0;
}

以上是关于Luogu P1600 天天爱跑步的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P1600 天天爱跑步 树上差分

Luogu P1600 天天爱跑步

luogu P1600 天天爱跑步

P1600 天天爱跑步

P1600 天天爱跑步

P1600 天天爱跑步