• 洛谷1600 天天爱跑步


    原题链接

    要解决这题有一个很重要的思想,就是将跑步的路径拆开来,分成向上走的(S o LCA(S,T)),及向下走的(LCA(S,T) o T)(S)是路径起点,(T)是路径终点)。
    然后对于两种路径单独统计贡献。
    先只考虑向上走的路径(S o LCA(S,T))
    对于一个观测点(i)来说,只有满足(deep[S]-w[i]=deep[i])(deep)表示点在树中的深度)的路径才能对它产生贡献。移项得(deep[S]=deep[i]+w[i]),于是我们可以对树进行(dfs),并统计。
    用一个桶(up)来统计当前贡献。
    当搜到第(i)个点时(显然它的子树都已经统计完了),先在桶里加入从该点出发的路径,即(up[deep[i]]+=SUM\_S[i])
    然后计算能对该点产生贡献的路径数,即(ans[i]+=up[deep[i]+w[i]])(注意(deep[i]+w[i])是可能越界的)
    在退出该点时,从桶里减去向上走的路径中到达该点就不往上走的路径,即该点就是(LCA(S,T)),向上走路径的终点。
    注意因为一棵树里有很多同一深度的点,我们统计的不一定是同一棵子树下的,所以我们可以在进入该点时记录下原始的(up[deep[i]+w[i]])值,在将子树搜索完后,用新的值减去原始值即是这棵子树所产生的贡献。
    而对于向下走的路径(LCA(S,T) o T)的统计方法类似,只有满足(deep[T]-deep[i]=length-w[i])(length)表示路径长度)的路径才能对它产生贡献。移项得(deep[T]-length=deep[i]-w[i]),于是我们可以相似的用桶在(dfs)中统计。
    当搜到第(i)个点时,一样先在桶里加入终点为该点的路径(因为(dfs)统计是向上统计,所以要加入终点),然后统计能对该点产生贡献的路径数。
    在退出该点时,从桶里减去向下走路径中起点为该点的路径,即该点是(LCA(S,T))
    注意(deep[i]-w[i])(deep[T]-length)可能是负数,所以需要加上一个数,平移数组。
    最后将重复计算的(LCA)的贡献减(1)即可。
    (LCA)部分可用倍增,或是(tarjan),这里我是用的倍增。

    #include<cstdio>
    #include<cmath>
    #include<vector>
    using namespace std;
    const int N = 3e5 + 10;
    const int M = N << 1;
    const int K = 3e5;
    struct ts {
    	int x, y, L, LCA;
    };
    ts a[N];
    int fi[N], di[M], ne[M], f[N][20], de[N], an[N], ss[N], up[N], dn[M], w[N], mdp, l, gn;
    vector<int>ule[N], dmo[N], dle[N];
    inline int re()
    {
    	int x = 0;
    	char c = getchar();
    	bool p = 0;
    	for (; c < '0' || c > '9'; c = getchar())
    		p |= c == '-';
    	for (; c >= '0' && c <= '9'; c = getchar())
    		x = x * 10 + c - '0';
    	return p ? -x : x;
    }
    inline void add(int x, int y)
    {
    	di[++l] = y;
    	ne[l] = fi[x];
    	fi[x] = l;
    }
    inline int maxn(int x, int y)
    {
    	return x > y ? x : y;
    }
    inline void sw(int &x, int &y)
    {
    	int z = x;
    	x = y;
    	y = z;
    }
    void dfs(int x)
    {
    	int i, y;
    	mdp = maxn(mdp, de[x]);
    	for (i = 1; i <= gn; i++)
    		f[x][i] = f[f[x][i - 1]][i - 1];
    	for (i = fi[x]; i; i = ne[i])
    		if ((y = di[i]) ^ f[x][0])
    		{
    			de[y] = de[x] + 1;
    			f[y][0] = x;
    			dfs(y);
    		}
    }
    int lca(int x, int y)
    {
    	int i;
    	if (de[x] > de[y])
    		sw(x, y);
    	for (i = gn; ~i; i--)
    		if (de[f[y][i]] >= de[x])
    			y = f[y][i];
    	if (!(x ^ y))
    		return x;
    	for (i = gn; ~i; i--)
    		if (f[x][i] ^ f[y][i])
    		{
    			x = f[x][i];
    			y = f[y][i];
    		}
    	return f[x][0];
    }
    void dfs_up(int x)
    {
    	int i, y, la, nw = w[x] + de[x], si = ule[x].size();
    	if (nw <= mdp)
    		la = up[nw];
    	for (i = fi[x]; i; i = ne[i])
    		if ((y = di[i]) ^ f[x][0])
    			dfs_up(y);
    	up[de[x]] += ss[x];
    	if (nw <= mdp)
    		an[x] = up[nw] - la;
    	for (i = 0; i < si; i++)
    		up[ule[x][i]]--;
    }
    void dfs_down(int x)
    {
    	int i, y, la, nw = de[x] - w[x], si_1 = dmo[x].size(), si_2 = dle[x].size();
    	la = dn[nw + K];
    	for (i = fi[x]; i; i = ne[i])
    		if ((y = di[i]) ^ f[x][0])
    			dfs_down(y);
    	for (i = 0; i < si_1; i++)
    		dn[dmo[x][i] + K]++;
    	an[x] += dn[nw + K] - la;
    	for (i = 0; i < si_2; i++)
    		dn[dle[x][i] + K]--;
    }
    int main()
    {
    	int i, n, m, x, y;
    	n = re();
    	m = re();
    	gn = log2(n);
    	for (i = 1; i < n; i++)
    	{
    		x = re();
    		y = re();
    		add(x, y);
    		add(y, x);
    	}
    	de[1] = 1;
    	dfs(1);
    	for (i = 1; i <= n; i++)
    		w[i] = re();
    	for (i = 1; i <= m; i++)
    	{
    		a[i].x = re();
    		a[i].y = re();
    		ss[a[i].x]++;
    		a[i].LCA = lca(a[i].x, a[i].y);
    		a[i].L = de[a[i].x] + de[a[i].y] - (de[a[i].LCA] << 1);
    		ule[a[i].LCA].push_back(de[a[i].x]);
    		dmo[a[i].y].push_back(de[a[i].y] - a[i].L);
    		dle[a[i].LCA].push_back(de[a[i].y] - a[i].L);
    	}
    	dfs_up(1);
    	dfs_down(1);
    	for (i = 1; i <= m; i++)
    		if (!((de[a[i].x] - w[a[i].LCA]) ^ de[a[i].LCA]))
    			an[a[i].LCA]--;
    	for (i = 1; i <= n; i++)
    		printf("%d ", an[i]);
    	return 0;
    }
    
  • 相关阅读:
    LINUX 逻辑地址、线性地址、物理地址和虚拟地址 转
    Linux开机执行顺序
    Linux内核分析笔记 与Linux内核开发理论
    理解Linux系统中的load average(图文版)转
    Linux文件空洞与稀疏文件 转
    Linux中断(interrupt)子系统之一:中断系统基本原理 (图解)
    LINUX 内存结构
    LINUX 数据结构 &算法 网络协议 & 网络编程 多任务编程
    IO端口和IO内存的区别 转
    Linux内核高端内存 转
  • 原文地址:https://www.cnblogs.com/Iowa-Battleship/p/9720563.html
Copyright © 2020-2023  润新知