zoukankan      html  css  js  c++  java
  • 【LOJ#2236】【洛谷P3258】松鼠的新家【LCA】【树上差分】

    题目大意:

    题目链接:
    洛谷:https://www.luogu.org/problem/P3258
    LOJ:https://loj.ac/problem/2236
    给出一棵树以及nn个点走的顺序,求每一个点会被经过几次。规定到达最后一个点的那一次不算。


    思路:

    这是一道在「省选斗兽场-树链剖分」的一道题目。
    本着背树剖板子心态来刷的。看完题后
    这不是一道树上差分sb题吗?????
    既然在树剖分类中,那就用树剖求LCA吧。
    在普通树剖中,我们会有这样一段程序

    void addrange(int x,int y,int k)
    {
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		Tree.update(1,id[top[x]],id[x],k);
    		x=fa[top[x]];
    	}
    	if (id[x]>id[y]) Tree.update(1,id[y],id[x],k);
    		else Tree.update(1,id[x],id[y],k);
    }
    

    我们发现,最终退出whilewhile循环时,xyxy两点必然位于同一条重链中。
    那么显然此时的LCA就是深度较浅的点。
    这样就可以用树剖O(logn)O(log n)求出LCA。而且常数很小。
    然后随便用树上差分搞一搞就可以了。
    但是要注意,从xy,yzx o y,y o z中,我们会把yy计算两次,这样就导致答案多了1。所以最终答案要减去1。
    同时第1,n1,n个点只会算1次,按理来说是不用减1的,但是题目要求最后一次到达第nn个点不用算,所以就依然要减1,而第一个点就不用了。
    时间复杂度O(nlogn)O(nlog n)


    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=300010;
    int n,tot,a[N],s[N],head[N],dep[N],son[N],fa[N],top[N],size[N];
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    void dfs1(int x,int f)
    {
    	fa[x]=f; dep[x]=dep[f]+1; size[x]=1;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int y=e[i].to;
    		if (y!=f)
    		{
    			dfs1(y,x);
    			if (size[y]>size[son[x]]) son[x]=y;
    			size[x]+=size[y];
    		}
    	}
    }
    
    void dfs2(int x,int tp)
    {
    	top[x]=tp;
    	if (son[x]) dfs2(son[x],tp);
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int y=e[i].to;
    		if (y!=fa[x] && y!=son[x]) dfs2(y,y);
    	}
    }
    
    void dfs3(int x)
    {
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int y=e[i].to;
    		if (y!=fa[x])
    		{
    			dfs3(y);
    			s[x]+=s[y];
    		}
    	}
    }
    
    int lca(int x,int y)
    {
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		x=fa[top[x]];
    	}
    	return dep[x]>dep[y]?y:x;
    }
    
    int main()
    { 
    	memset(head,-1,sizeof(head));
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	}
    	dfs1(1,0); dfs2(1,1);
    	for (int i=1;i<n;i++)
    	{
    		int LCA=lca(a[i],a[i+1]);
    		s[a[i]]++; s[a[i+1]]++;
    		s[LCA]--; s[fa[LCA]]--;
    	}
    	dfs3(1); s[a[1]]++;  //第一个点不用减1
    	for (int i=1;i<=n;i++)
    		printf("%d
    ",s[i]-1);
    	return 0;
    }
    
  • 相关阅读:
    iaas,paas,saas理解
    July 06th. 2018, Week 27th. Friday
    July 05th. 2018, Week 27th. Thursday
    July 04th. 2018, Week 27th. Wednesday
    July 03rd. 2018, Week 27th. Tuesday
    July 02nd. 2018, Week 27th. Monday
    July 01st. 2018, Week 27th. Sunday
    June 30th. 2018, Week 26th. Saturday
    June 29th. 2018, Week 26th. Friday
    June 28th. 2018, Week 26th. Thursday
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998048.html
Copyright © 2011-2022 走看看