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;
    }
    
  • 相关阅读:
    webstorm 格式化代码及常用快捷键
    Jetbrains 2018 等系列软件激活破解除去黄色警告框方法(含多个平台)
    HTML5 Shiv完美解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
    为什么macos开机黑屏但是有声音?
    mac 回车键、空格键失灵(非物理原因)解决方法
    在VS代码中使用版本控制
    在Visual Studio代码中使用Flask
    [IOI1999]花店橱窗布置(DP路径记录)
    leetcode:Minimum Path Sum(路线上元素和的最小值)【面试算法题】
    c/c++ 复习基础要点01-const指针、指针函数 函数指针、new/delete与malloc/free区别与联系
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998048.html
Copyright © 2011-2022 走看看