• 天天爱跑步 题解


    天天爱跑步 题解

    原文链接:https://www.cnblogs.com/lfyzoi/p/10221884.html (如果真的要看,建议去原文,有图)

    LCA + 桶 + 树上差分

    Step1

    先对每一个运动员的路径求LCA, 枚举每个观察员,看有哪些节点在第(w_i)秒会为这个观察员做贡献。

    int f[maxn][30], dep[maxn];
    void dfs(int u, int fa)
    {
    		for(int i = head[u]; i; i = e[i].nxt)
    		{
    			int v = e[i].v;
    			if(v != fa) { f[v][0] = u; dep[v] = dep[u] + 1; dfs(v, u);}
    		}
    }
    void bz() 
    {
    	for(int j = 1; j <= 20; j++)
    	{
    		for(int i = 1; i <= n;i++) f[i][j] = f[f[i][j-1]][j-1];
    	}	
    }
    int lca(int x, int y)
    {
    	if(dep[x] < dep[y]) swap(x,y);
    	while(dep[x] > dep[y])
    	{
    		int z = log2(dep[x]-dep[y]);
    	    x = f[x][z];
    	}
    	if(x == y)  return x; 
    	for(int j = log2(dep[x]); j >= 0; j--)
    	{
    		if(f[x][j] != f[y][j]) { x = f[x][j]; y = f[y][j];}
    	}
    	return f[x][0];
    }
    

    Step2

    对于节点P,该如何判断这名选手会不会为P做贡献呢?

    分情况讨论:

    1. 如果P点是在这名选手从起点到LCA的路上,

      有一个结论, 当(deep[s_i] = w[P] + deep[P])时,这名运动员可以为P点做贡献。

      (deep[s_i] = deep[P] + dis[s_i][P]) , 如果(w[p] == dis[s_i][P]), 即(deep[s_i] = w[P] + deep[P])

      这名运动员可以为P点做贡献)。

    2. 如果P点是在这名选手从LCA到终点的路上,

      (dis[s_i][t_i] - w[P] = deep[t_i] - deep[P]), 即(dis[s_i][t_i] - deep[t_i] = w[P] - deep[P]), 时,

    ​ 这名运动员可以为P点做贡献。

    总结:上行过程中,满足条件的起点可以做贡献,下行过程中,满足条件的终点可以做贡献,但无论是哪一种情形,能对 P 做贡献的起点或终点一定都在以P为根的子树上(一定会经过P点),这使得可以在DFS回溯的过程中处理以任意节点为根的子树。

    Step3

    开始糊了

    递归以P为根的子树时,可以统计子树中所有的起点与终点对它的贡献。

    子树中有的起点和终点对P产生了贡献,有些不对P产生贡献,但对其他结点产生了贡献。

    所以我们不能枚举每个点,找子树中哪些点对其产生贡献,这样复杂度就上去了。

    而是对于树上的任何一个起点和终点,把其产生的贡献放在桶里面,回溯到子树根的时候再到桶里面查

    询结果。

    对于一个点P来说,以P为根递归整颗子树过程中在桶内产生的差值才是有效的

    还有一种情况,对于以P为根的内部路径(不经过P)的起点和终点产生的贡献是不应该属于P的。

    所以dfs过程中,在统计当前结点作为起点和终点所产生的贡献后,继而计算出当前结作为“根”上的差值后,在回溯过程中,一定要减去以当前结点为LCA的起点、终点在桶里产生的贡献,这部分贡献在离开这个子树后就没有意义了。

    如果路径起点或终点刚好为LCA且LCA处是可观察到运动员的,那么我们在上行统计过程中和下行统计过程中都会对该LCA产生贡献,这样就重复计数一次!

    好在这种情况很容易发现,我们提前预测到,对相应的结点进行ans[x]--即可。

    此外,在使用第二个桶时,下标是w[x]-deep[x]会成为负数,所以使用第二个桶时,下标统一+SIZE,向右平移一段区间,防止下溢。

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #define orz cout << "AK IOI" <<"
    "
    
    using namespace std;
    const int maxn = 300000;
    
    inline int read()
    {
    	int x = 0, f = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    	while(ch >= '0' && ch<= '9') { x = x * 10 + ch - '0'; ch = getchar();}
    	return x*f;
    }
    int n, m, w[maxn], dis[maxn], cnt[maxn], ans[maxn];
    struct node{
    	int u, v, nxt;
    }e[maxn << 1], e1[maxn << 1], e2[maxn << 1];
    
    int js, head[maxn];
    void add(int u, int v)
    {
    	e[++js] = (node){u, v, head[u]};
    	head[u] = js;
    }
    
    int f[maxn][30], dep[maxn];
    void dfs(int u, int fa)
    {
    		for(int i = head[u]; i; i = e[i].nxt)
    		{
    			int v = e[i].v;
    			if(v != fa) { f[v][0] = u; dep[v] = dep[u] + 1; dfs(v, u);}
    		}
    }
    void bz() 
    {
    	for(int j = 1; j <= 20; j++)
    	{
    		for(int i = 1; i <= n;i++) f[i][j] = f[f[i][j-1]][j-1];
    	}	
    }
    int lca(int x, int y)
    {
    	if(dep[x] < dep[y]) swap(x,y);
    	while(dep[x] > dep[y])
    	{
    		int z = log2(dep[x]-dep[y]);
    	    x = f[x][z];
    	}
    	if(x == y)  return x; 
    	for(int j = log2(dep[x]); j >= 0; j--)
    	{
    		if(f[x][j] != f[y][j]) { x = f[x][j]; y = f[y][j];}
    	}
    	return f[x][0];
    }
    //--------------------------------------------------------------------------------------------------
    struct peo{
    	int s, t;
    }a[maxn];
    int js1, js2, head1[maxn], head2[maxn];
    void add1(int u, int v)
    {
    	e1[++js1] = (node){u, v, head1[u]};
    	head1[u] = js1;
    }
    void add2(int u, int v)
    {
    	e2[++js2] = (node){u, v, head2[u]};
    	head2[u] = js2;
    }
    int b1[maxn << 1], b2[maxn << 1]; 
    void dfs2(int x)
    {
    	int t1 = b1[w[x] + dep[x]], t2 = b2[w[x] - dep[x] + maxn];//读桶取里的数值,t1是上行桶里的值,t2是下行桶的值
    	for(int i = head[x]; i; i = e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v == f[x][0]) continue;
    		dfs2(v);
    	}
    	b1[dep[x]] += cnt[x];//上行过程中,当前节点作为起点产生的贡献
    	for(int i = head1[x]; i; i = e1[i].nxt)//下行过程中,当前节点作为终点产生的贡献
    	{
    		int v = e1[i].v;
    		b2[dis[v] - dep[a[v].t] + maxn]++;
    	} 
    	ans[x] += b1[w[x] + dep[x]] - t1 + b2[w[x] - dep[x] + maxn] - t2;//计算上、下行桶内差值
    	for(int i = head2[x]; i; i = e2[i].nxt)//清除 
    	{
    		int v = e2[i].v;
    		b1[dep[a[v].s]]--;
    		b2[dis[v] - dep[a[v].t] + maxn]--; 
    	}
    }
    int main()
    {
    	n = read();  m = read();
    	for(int i = 1; i < n; i++)
    	{
    		int x = read(), y = read();
    		add(x, y), add(y, x);
    	}
    	dep[1] = 1; f[1][0] = 1; dfs(1, -1); bz();
    	for(int i = 1; i <= n; i++) w[i] = read();
    	for(int i = 1; i <= m; i++) 
    	{
    		a[i].s = read(), a[i].t = read();
    		int LCA = lca(a[i].t, a[i].s);
    		dis[i] = dep[a[i].s] + dep[a[i].t] - 2 * dep[LCA];
    		cnt[a[i].s]++;                          //便于统计上行过程中该节点的贡献 
    		add1(a[i].t, i), add2(LCA, i); //把第i条路径加到以t[i]为终点的路径集合中
    		if(dep[LCA] + w[LCA] == dep[a[i].s]) ans[LCA]--; 
     	}
    	dfs2(1);
    	for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Arrays常用方法
    一篇很好的java异常框架讲解
    python网络数据采集(低音曲)
    python网络数据采集(伴奏曲)
    记一次python的一些参数
    webshell 生成工具 b374k
    c/s与b/s 动态网站与静态网站 (网站编码统一“UTF-8”)
    RED_HAWK:基于PHP实现的信息收集与SQL注入漏洞扫描工具
    CVE-2017-8464复现 (远程快捷方式漏洞)
    cobaltstrike安装加破解教程+使用教程
  • 原文地址:https://www.cnblogs.com/yangchengcheng/p/14882928.html
Copyright © 2020-2023  润新知