洛谷P1600 天天爱跑步

1 篇文章 0 订阅
1 篇文章 0 订阅

题目描述

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

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

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

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

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

输入输出格式

输入格式:

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

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

接下来一行 nn 个整数,其中第 jj 个整数为 W_jWj , 表示结点 jj 出现观察员的时间。

接下来 mm 行,每行两个整数 S_iSi ,和 T_iTi ,表示一个玩家的起点和终点。

对于所有的数据,保证 1\leq S_i,T_i\leq n, 0\leq W_j\leq n1Si,Tin,0Wjn 。

输出格式:

输出1行 nn 个整数,第 jj 个整数表示结点 jj 的观察员可以观察到多少人。

输入输出样例

输入样例#1:  复制
6 3
2 3
1 2 
1 4 
4 5 
4 6 
0 2 5 1 2 3 
1 5 
1 3 
2 6 
输出样例#1:  复制
2 0 0 1 1 1 
输入样例#2:  复制
5 3 
1 2 
2 3 
2 4 
1 5 
0 1 0 3 0 
3 1 
1 4
5 5 
输出样例#2:  复制
1 2 1 0 1 

说明

【样例1说明】

对于 11 号点, W_i=0Wi=0 ,故只有起点为1号点的玩家才会被观察到,所以玩家 11 和玩家 22 被观察到,共有 22 人被观察到。

对于 22 号点,没有玩家在第 22 秒时在此结点,共 00 人被观察到。

对于 33 号点,没有玩家在第 55 秒时在此结点,共 00 人被观察到。

对于 44 号点,玩家 11 被观察到,共 11 人被观察到。

对于 55 号点,玩家 11 被观察到,共 11 人被观察到。

对于 66 号点,玩家 33 被观察到,共 11 人被观察到。

【子任务】

每个测试点的数据规模及特点如下表所示。 提示: 数据范围的个位上的数字可以帮助判断是哪一种数据类型。

【提示】

如果你的程序需要用到较大的栈空问 (这通常意味着需要较深层数的递归), 请务必仔细阅读选手日录下的文本当rumung:/stact.p″, 以了解在最终评测时栈空问的限制与在当前工作环境下调整栈空问限制的方法。

在最终评测时,调用栈占用的空间大小不会有单独的限制,但在我们的工作环境中默认会有 8 MB8MB 的限制。 这可能会引起函数调用层数较多时, 程序发生栈溢出崩溃。

我们可以使用一些方法修改调用栈的大小限制。 例如, 在终端中输入下列命令 ulimit -s 1048576

此命令的意义是,将调用栈的大小限制修改为 1 GB1GB 。

例如,在选手目录建立如下 sample.cpp 或 sample.pas

将上述源代码编译为可执行文件 sample 后,可以在终端中运行如下命令运行该程序

./sample

如果在没有使用命令“ ulimit -s 1048576”的情况下运行该程序, sample会因为栈溢出而崩溃; 如果使用了上述命令后运行该程序,该程序则不会崩溃。

特别地, 当你打开多个终端时, 它们并不会共享该命令, 你需要分别对它们运行该命令。

请注意, 调用栈占用的空间会计入总空间占用中, 和程序其他部分占用的内存共同受到内存限制。



题解:

思路还是挺好想的,把每个玩家的路拆成起点(S)到lca和从lca到终点(T)两条。

先考虑从S到lca的路径。我们发现如果某个玩家对i号点有贡献,那么dep[s]==dep[i]+w[i](这更好想了,随便画画图或推一下就知道了的)。

从lca到t的路径如果对某个点有贡献则是(dep[s]-dep[lca])(从起点到lca的时间)-dep[lca]==w[i]-dep[i](这点同上)。

然后差分即可。


PS:打代码时遇到各种各样的问题。前边没想到每个点差分桶里只要保存一个信息,然后又没想到怎么处理经过某一个点的玩家数,最后又没考虑lca被统计了两次的情况。。


代码:

#include<bits/stdc++.h>
using namespace std; 
int n,m,a[1000001],f[1000001],dep[1000001],fa[300001][21],e[1000001],next[1000001],head[1000001],tot,cnt,cnt1,e1[1000001],head1[1000001],head2[1000001],e2[1000001],next1[1000001],next2[1000001],jia1[1000001],jia2[1000001],ans[1000001];
void build(int t,int k){
    tot++;
    e[tot]=k;
    next[tot]=head[t];head[t]=tot;
}
void add(int t,int k,int s){
    cnt++;
    e1[cnt]=k;jia1[cnt]=s;
    next1[cnt]=head1[t];head1[t]=cnt;
}
void add1(int t,int k,int s){
    cnt1++;
    e2[cnt1]=k;jia2[cnt1]=s;
    next2[cnt1]=head2[t];head2[t]=cnt1;
}
void dfs(int x,int ffa,int shen){
    int i;
    dep[x]=shen;
    for(i=head[x];i;i=next[i]){
        if(e[i]==ffa)continue;
        dfs(e[i],x,shen+1);
        fa[e[i]][0]=x;
    }
}
int LCA(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    int i;
    for(i=20;~i;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    //printf("%d %d\n",x,y);
    if(x==y)return x;
    for(i=20;~i;i--)if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];y=fa[y][i];
        }
    return fa[x][0];		
}
void dfsxtos(int x,int ffa,int shen){
    int i,x1,la;
    x1=dep[x]+a[x];
    //printf("%d %d %d %d\n",x,shen,a[x],f[x1]);
    la=f[x1];
    for(i=head[x];i;i=next[i]){
        if(e[i]==ffa)continue;
        dfsxtos(e[i],x,shen+1);
    }
    for(i=head1[x];i;i=next1[i]){
        if(jia1[i]==1){
            f[e1[i]]++;
        }
    }
    ans[x]+=f[x1]-la;
    //printf("%d %d %d %d\n",x,shen,a[x],f[x1]);
    for(i=head1[x];i;i=next1[i]){
        if(jia1[i]==-1)f[e1[i]]--;
    }
}
void dfsstox(int x,int ffa,int shen){
    int i,x1,la;
   // printf("%d\n",x);
    x1=a[x]-dep[x]+500000;
    for(i=head2[x];i;i=next2[i]){
        if(jia2[i]==1){
        	//printf("%d ",e2[i]);
            f[e2[i]]++;
        }
    }
    la=f[x1];
    //puts("");
    //ans[x]+=f[x1];
    for(i=head2[x];i;i=next2[i]){
        if(jia2[i]==-1){
        	//printf("%d ",e2[i]);
         f[e2[i]]--;
     }
    }
   // puts("");
    for(i=head[x];i;i=next[i]){
        if(e[i]==ffa)continue;
        dfsstox(e[i],x,shen+1);
}
 ans[x]+=la-f[x1];
}
int main(){
    int i,j,t,k,lca;
    scanf("%d%d",&n,&m);
    for(i=1;i<n;i++){
        scanf("%d%d",&t,&k);
        build(t,k);
        build(k,t);
    }
    dfs(1,-1,1);
    for(j=1;j<=20;j++)
     for(i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    for(i=1;i<=m;i++){
        scanf("%d%d",&t,&k);
        lca=LCA(t,k);
        //printf("%d %d %d\n",t,k,lca);
        if(lca==k){
            add(t,dep[t],1);
            add(lca,dep[t],-1);
            continue;
        }
        if(lca==t){
            add1(lca,500000-dep[lca],1);
            add1(k,500000-dep[lca],-1);
            continue;
        }
        add(t,dep[t],1);
        add(lca,dep[t],-1);
        add1(lca,dep[t]-dep[lca]-dep[lca]+500000,1);
        add1(k,dep[t]-dep[lca]-dep[lca]+500000,-1);
        if(a[lca]+dep[lca]==dep[t])ans[lca]--;
    }
    dfsxtos(1,-1,1);
    memset(f,0,sizeof(f));
    dfsstox(1,-1,1);
    for(i=1;i<=n;i++)printf("%d ",ans[i]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值