• luogu P1600 天天爱跑步 |树上差分+LCA


    题目描述

    小c 同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

    这个游戏的地图可以看作一一棵包含 nnn 个结点和 n−1n-1n−1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 111 到 nnn 的连续正整数。

    现在有 mmm 个玩家,第 iii 个玩家的起点为 sis_isi​,终点为 tit_iti​。每天打卡任务开始时,所有玩家在第 000 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树,所以每个人的路径是唯一的)

    小c 想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 jjj 的观察员会选择在第 wjw_jwj​ 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 wjw_jwj​ 秒也理到达了结点 jjj 。小c 想知道每个观察员会观察到多少人?

    注意:我们认为一个玩家到达自己的终点后该玩家就会结束游戏,他不能等待一 段时间后再被观察员观察到。 即对于把结点 jjj 作为终点的玩家:若他在第 wjw_jwj​ 秒前到达终点,则在结点 jjj 的观察员不能观察到该玩家;若他正好在第 wjw_jwj​ 秒到达终点,则在结点 jjj 的观察员可以观察到这个玩家。

    输入格式

    第一行有两个整数 nnn 和 mmm。其中 nnn 代表树的结点数量, 同时也是观察员的数量, mmm 代表玩家的数量。

    接下来 n−1n-1n−1 行每行两个整数 uuu 和 vvv,表示结点 uuu 到结点 vvv 有一条边。

    接下来一行 nnn 个整数,其中第 jjj 个整数为 wjw_jwj​ , 表示结点 jjj 出现观察员的时间。

    接下来 mmm 行,每行两个整数 sis_isi​,和 tit_iti​,表示一个玩家的起点和终点。

    对于所有的数据,保证 1≤si,ti≤n,0≤wj≤n1leq s_i,t_ileq n, 0leq w_jleq n1≤si​,ti​≤n,0≤wj​≤n。

    输出格式

    输出 111 行 nnn 个整数,第 jjj 个整数表示结点 jjj 的观察员可以观察到多少人。


    【题解】洛谷1600天天爱跑步

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int N=6e5+10;
    inline int read(){
    	int x=0; char c=getchar();
    	while(c<'0'||c>'9')c=getchar();
    	while('0'<=c&&c<='9'){ x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
    	return x;
    }
    int nxt[N<<1],head[N],go[N<<1],tot;
    inline void add(int u,int v){nxt[++tot]=head[u],head[u]=tot,go[tot]=v,nxt[++tot]=head[v],head[v]=tot,go[tot]=u;}
    int n,m,w[N],f[N][17];
    struct node{
    	int x,lca,op;
    };
    vector<node>a[N];
    int dep[N];
    void deal(int u,int fa){
    	dep[u]=dep[fa]+1;
    	for(int i=0;i<16;i++)f[u][i+1]=f[f[u][i]][i];
    	for(int i=head[u];i;i=nxt[i]){
    		int v=go[i];
    		if(v==fa)continue;
    		f[v][0]=u;
    		deal(v,u);
    	}
    }
    #define _ 10000
    inline int LCA(int x,int y){
    	if(dep[x]<dep[y])swap(x,y);
    	for(int i=16;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i];
    	if(x==y)return x;
    	for(int i=16;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    int cnt1[N],cnt2[N],ans[N];
    vector<int>del1[N],del2[N];
    void dfs(int u,int fa){
    	int x=cnt1[_+dep[u]+w[u]],y=cnt2[_+w[u]-dep[u]];
    	for(int i=head[u];i;i=nxt[i]){
    		int v=go[i];
    		if(v==fa)continue;
    		dfs(v,u);
    	}
    	for(int i=0;i<a[u].size();i++){
    		int x=a[u][i].x,lca=a[u][i].lca;
    		if(a[u][i].op){
    			cnt1[_+dep[u]]++;
    			del1[lca].push_back(dep[u]);
    		}
    		else {
    			cnt2[_+dep[x]-2*dep[lca]]++;
    			del2[lca].push_back(dep[x]-2*dep[lca]);
    		}
    	}
    	for(int i=0;i<del1[u].size();i++)cnt1[_+del1[u][i]]--;
    	ans[u]=cnt1[_+dep[u]+w[u]]-x+cnt2[_+w[u]-dep[u]]-y;
    	for(int i=0;i<del2[u].size();i++)cnt2[_+del2[u][i]]--;
    }
    signed main(){
    	n=read(),m=read();
    	for(int i=1;i<n;i++)add(read(),read());
    	deal(1,1);
    	for(int i=1;i<=n;i++)w[i]=read();
    	for(int i=1,x,y,z;i<=m;i++){
    		x=read(),y=read();
    		z=LCA(x,y);
    		a[x].push_back((node){y,z,1});
    		a[y].push_back((node){x,z,0});
    	}
    	dfs(1,0);
    	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    	printf("
    ");
    	return 0;
    }
    
  • 相关阅读:
    数组按行访问和按列访问的区别
    解决github中图片无法显示问题的策略
    C#验证帮助类(手机号、身份证等)
    WPF圆角按钮样式
    弗洛伊德的本我、自我与超我概念
    KEPServerEX OPCUA服务器地址配置
    C#List的一些操作
    梦、清醒梦、梦魇
    类说明head部格式
    阅读笔记20222
  • 原文地址:https://www.cnblogs.com/naruto-mzx/p/12150157.html
Copyright © 2020-2023  润新知