测试地址:天天爱跑步
做法:这里转载一下我看的题解:点这里,这里面对于整个题的做法应该写的很明确了,这里就不再赘述了。我的做法是实时用倍增求出路径两点的LCA(当然也可以离线用Tarjan做,貌似快一点),然后用类似邻接表的链表结构存储上面题解里面的“人”,结构体里有四个参数:t,d,p,next,t就是指出发的时间,d是指这个人是一个+1的人还是一个-1的人(值为1或-1),p是指这个人是一个从当前点走向根的人(为0)还是一个从根走向当前点的人(为1),next就指向与当前点有联系的下一个人,注意每一条路径都会产生这样的4个人,数组要开够。然后注意如果对于每个节点都开一个桶来存储以该结点为根的子树上各权值的人数的话,给你几十G的内存都没法做,这时就只用开两个桶,一个桶存储目前为止走向根的人中各权值的人数,另一个桶存储目前为止从根出发的人中各权值的人数,然后在DFS到一个节点时,先把与这个点关联的两个权值上的人数记录下来,在对其子树进行DFS完后再用桶中的值减掉这两个值,这样就可以保证留下来的信息都是子树上的信息。最后注意算出来的下标可能为负数,把所有下标加个n变成非负数即可。
以下是本人代码:
#include
#include
#include
#include
#include
using namespace std;
int n,m,w[300010],dep[300010]={0},first[300010]={0},firstq[300010]={0},tot=0;
int fa[300010][20]={0},bucket1[1000010]={0},bucket2[1000010]={0},ans[300010]={0};
bool vis[300010]={0};
struct edge {int v,next;} e[600010];
struct path {int t,d,next;bool p;} q[1200010];
void insert(int a,int b)
{
e[++tot].v=b,e[tot].next=first[a],first[a]=tot;
}
void insertq(int a,int t,int d,bool p)
{
q[++tot].t=t,q[tot].d=d,q[tot].p=p,q[tot].next=firstq[a],firstq[a]=tot;
}
void dfs(int v)
{
vis[v]=1;
for(int i=first[v];i;i=e[i].next)
if (!vis[e[i].v])
{
dep[e[i].v]=dep[v]+1;
fa[e[i].v][0]=v;
dfs(e[i].v);
}
}
int lca(int x,int y)
{
if (dep[x]=0;i--)
if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
for(int i=19;i>=0;i--)
if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void find_ans(int v)
{
int x1,val1,x2,val2;
x1=w[v]+dep[v]+n,x2=w[v]-dep[v]+n;
val1=bucket1[x1],val2=bucket2[x2];
for(int i=firstq[v];i;i=q[i].next)
{
if (!q[i].p) bucket1[q[i].t+dep[v]+n]+=q[i].d;
else bucket2[q[i].t+n]+=q[i].d;
}
for(int i=first[v];i;i=e[i].next)
if (dep[e[i].v]>dep[v]) find_ans(e[i].v);
ans[v]=bucket1[x1]+bucket2[x2]-val1-val2;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,a,b;i