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 天天爱跑步的主要内容,如果未能解决你的问题,请参考以下文章