zoukankan      html  css  js  c++  java
  • NOIP2016天天爱跑步

    NOIP2016 天天爱跑步

    题意:有一棵(n)个点的树,有(m)个玩家,每个玩家有一个起点(S_i,T_i),所有的玩家在第(0)秒从自己的起点出发,以每秒跑一条边的速度,沿着最短路径向终点跑。节点(j)的观察员会在第(W_j)秒观察此时该节点上的玩家,求每个观察员会观察到多少人?

    题目链接

    数据范围:(1<=n,m<=3e5)

    解法:

    (LCA) + 桶 + 差分

    先预处理出每个节点的深度(dep_i),以及每条路径的长度(dis_i)(用倍增(LCA))。

    这里我们如果考虑观察员,会发现时间复杂度很难优化。所以我们考虑每个玩家对答案的贡献。

    我们先把每条路径拆成向上的一段和向下的一段(最后的时候要注意起点和终点是否会对(LCA)重复贡献)

    向上:考虑位于节点(j)的观察员,当且仅当(S_i)位于(j)的子树中且(S_i,T_i)的LCA不在(j)之上时,如果(dep_{s_i} = dep_j+W_j),则(S_i)会对(j)产生贡献。

    向下同理,式子变为(dis_i-(dep_{T_i}-dep_j)=W_j)时会对(j)产生贡献。

    我们用桶记录,每次出一个节点时用桶统计答案,并减掉这个点最为(LCA)时对这个点以上的点的答案的影响。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define maxn 300100
    int n, m;
    int w[maxn], dep[maxn], dis[maxn];
    int f1[maxn], f2[maxn * 2];
    int st[maxn], ed[maxn], cnt[maxn], ans[maxn];
    int f[maxn][22];
    int fir[maxn], nxt[maxn * 2], vv[maxn * 2];
    int fir1[maxn], nxt1[maxn * 2], vv1[maxn * 2];
    int fir2[maxn], nxt2[maxn * 2], vv2[maxn * 2];
    int tot, tot1, tot2;
    void add(int u, int v)
    {
    	nxt[++tot] = fir[u]; fir[u] = tot; vv[tot] = v; return;
    }
    void add1(int u, int v)
    {
    	nxt1[++tot1] = fir1[u]; fir1[u] = tot1; vv1[tot1] = v; return;
    }
    void add2(int u, int v)
    {
    	nxt2[++tot2] = fir2[u]; fir2[u] = tot2; vv2[tot2] = v; return;
    }
    void Deal_first(int u, int fa)
    {
    	dep[u] = dep[fa] + 1;
    	for(int i = 0; i <= 19; i++)
        	f[u][i + 1] = f[f[u][i]][i];
    	for(int i = fir[u]; i; i = nxt[i])
    	{
    		int v = vv[i];
    		if(v == fa) continue;
    		f[v][0] = u;
    		Deal_first(v, u);
    	}
    	return;
    }
    int LCA(int x, int y)
    {
    	if(dep[x] < dep[y]) swap(x, y);
    	for(int i = 20; i >= 0; i--)
    	{
    		if(dep[f[x][i]] >= dep[y])
    		{
    			x = f[x][i];
    		}
    		if(x == y)
    		{
    			return x;
    		}
    	}
    	for(int i = 20; i >= 0; i--)
    	{
    		if(f[x][i] != f[y][i])
            {
            	x = f[x][i];
            	y = f[y][i];
    		}
    	}
    	return f[x][0];
    }
    void dfs(int u, int fa)
    {
    //	printf("u = %d fa = %d
    ", u, fa);
    	int tmp1 = f1[w[u] + dep[u]], tmp2 = f2[w[u] - dep[u] + maxn];
    	for(int i = fir[u]; i; i = nxt[i])
    	{
    		int v = vv[i];
    		if(v == fa) continue;
    		dfs(v, u);
    	}
    	f1[dep[u]] += cnt[u];//以u为起点
    	for(int i = fir1[u]; i; i = nxt1[i])// 枚举以u为终点的情况 因为每个终点的dis值不同,故须记录一个链式前向星
    	{
    		int v = vv1[i];
    //	    f2[dis[v] - dep[u] + maxn] += 1;
            f2[dis[v] - dep[ed[v]] + maxn] += 1;
    	}
    	ans[u] += (f1[w[u] + dep[u]] - tmp1) + (f2[w[u] - dep[u] + maxn] - tmp2);
    	for(int i = fir2[u]; i; i = nxt2[i])
    	{
    		int v = vv2[i];
    		f1[dep[st[v]]] -= 1;
    		f2[dis[v] - dep[ed[v]] + maxn] -= 1;
    	}
    	return;
    }
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i < n; i++)
    	{
    		int u, v; scanf("%d%d", &u, &v);
    		add(u, v); add(v, u);
    	}
    	for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
    	Deal_first(1, 1);  dep[1] = 1;// printf("makiah");
    	for(int i = 1; i <= m; i++)
    	{
    		scanf("%d%d", &st[i], &ed[i]);
    		int fa = LCA(st[i], ed[i]);
    	//	printf("fa = %d
    ", fa);
    		dis[i] = dep[st[i]] + dep[ed[i]] - dep[fa] * 2;
    		cnt[st[i]]++;
    		add1(ed[i], i);
    		add2(fa, i);
    		if(dep[fa] + w[fa] == dep[st[i]]) ans[fa]--;
    	}
    	dfs(1, 0);
    	for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    SSAS aggregation 的作用及其使用
    liblinear使用总结
    python绝对路径相对路径函数
    libsvm使用总结
    一次scrapy成功停止的信息
    简单总结scrapy使用方法
    python编码格式
    一次scrapy失败的提示信息:由于连接方在一段时间后没有正确答复或连接的主机没有反 应,连接尝试失败
    17.1 MySQL主从介绍 17.2 准备工作 17.3 配置主 17.4 配置从 17.5 测试主从同步
    16.1 Tomcat介绍 16.2 安装jdk 16.3 安装Tomcat
  • 原文地址:https://www.cnblogs.com/Akaina/p/11751807.html
Copyright © 2011-2022 走看看