[NOIP2016]天天爱跑步

1 篇文章 0 订阅

NOIP2016 Day1T3 洛谷P1600 天天爱跑步

题目描述

  小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。
这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从到的连续正整数。
现在有个玩家,第个玩家的起点为 ,终点为 。每天打卡任务开始时,所有玩家在第秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)
  小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点的观察员会选择在第秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第秒也理到达了结点 。 小C想知道每个观察员会观察到多少人?
  注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点作为终点的玩家: 若他在第秒重到达终点,则在结点的观察员不能观察到该玩家;若他正好在第秒到达终点,则在结点的观察员可以观察到这个玩家。
输入输出格式

输入格式:

  第一行有两个整数和 。其中代表树的结点数量, 同时也是观察员的数量, 代表玩家的数量。
  接下来 行每行两个整数和 ,表示结点 到结点 有一条边。
  接下来一行 个整数,其中第个整数为 , 表示结点出现观察员的时间。
  接下来 行,每行两个整数,和,表示一个玩家的起点和终点。

输出格式:

  输出1行 个整数,第个整数表示结点的观察员可以观察到多少人。
输入输出样例
输入样例#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说明】

  对于1号点,,故只有起点为1号点的玩家才会被观察到,所以玩家1和玩家2被观察到,共有2人被观察到。
  对于2号点,没有玩家在第2秒时在此结点,共0人被观察到。
  对于3号点,没有玩家在第5秒时在此结点,共0人被观察到。
  对于4号点,玩家1被观察到,共1人被观察到。
  对于5号点,玩家1被观察到,共1人被观察到。
  对于6号点,玩家3被观察到,共1人被观察到。

解题报告:

  还记得当时不会LCA,就随便写了个暴力水了水。现在在回来看这个题,竟然卡了一会儿。
  在网上翻了翻关于这个题的题解,都写的很混乱,很多Dalao表示看了很多题解都看不懂到底怎么做。
  具体算法:Tarjan LCA + 树上差分 + 桶
  如果我们把目光跟着人跑的话,那这个题就GG爆零了。我们需要把目光放在观察员身上。
  如果观察员i能够观察到玩家A,那么玩家A的起点或终点一定在i的子树中。设u为起点,v为终点,len为路径的长度。
情况一
  A的起点u在i的子树中,i若能看到A,那么一定满足等式:
  

Deep[x]Deep[i]=Time[i]

  移项得:
Deep[i]+Time[i]=Deep[x]

   我们发现左边是个定值!
情况二
  A的终点v在i的子树中,i看到A,那么一定满足等式:
  
Deep[v]Deep[i]=lenTime[i]

  移项得:
  
Time[i]Deep[i]=lenDeep[v]

  你猜的没错, 左边还是一个定值
  这就启发我们可以用桶。开一个全局的桶,在DFS的时候,对于点i,进入i时和出i时,i在桶中所对应的两个定值点的增加量就是i的观察数。
  但有些同学就会问了,可是如果出现 uv 都在i的子树里,而路径并不经过i,这种情况不就错了吗?
  所以,我们需要进行树上差分。在 uv 点贡献答案后,回溯到 Lca(u,v) 时再将贡献清除,这样就能保证正确。
  另一方面,由于我们知道所有需要求 Lca 的点,所以我们用Tarjan去求 Lca 最优美。
  这样最后的时间复杂度是 O(n+m) ,复杂度很让人开心。
  不过,具体落实代码的时候还是会有很多细节的,细节就不在这说了,需要你们自己去探索。

附上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define o 410001
using namespace std;
int deep[o],head[o],nxt[o*2],point[o*2],U[o],sum[o*2][2],father[o],ancestor[o],lca[o*2],sign[o],ans[o],look[o];
struct node{int nxt,point;bool beginisU;}E[o*2];
struct sumu{int nxt,point,len;}lis[o*2];
int n,m,tot,cnt=1,zoom,Move=310000;
bool vis[o],man[o],haha[o*2];
int find(int x){
    if(father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
void unionn(int x,int y){
    father[find(y)]=find(x);
}
void addedge(int x,int y){
    tot++;nxt[tot]=head[x];head[x]=tot;point[tot]=y;
    tot++;nxt[tot]=head[y];head[y]=tot;point[tot]=x;
}
void Add(int u,int v){
    cnt++;E[cnt].nxt=U[u];U[u]=cnt;E[cnt].point=v;E[cnt].beginisU=true;
    cnt++;E[cnt].nxt=U[v];U[v]=cnt;E[cnt].point=u;E[cnt].beginisU=false;
}
void makesign(int point,int now,int len){
    zoom++;lis[zoom].nxt=sign[now];sign[now]=zoom;lis[zoom].point=point;lis[zoom].len=len;
}
void Tar(int now,int dad){
    father[now]=now;
    ancestor[now]=now;
    for(int tmp=head[now];tmp;tmp=nxt[tmp]){
        int u=point[tmp];
        if(!vis[u]&&u!=dad){
            deep[u]=deep[now]+1;
            Tar(u,now);
            unionn(now,u);
            ancestor[find(now)]=now;
        }
    }
    vis[now]=true;
    for(int tmp=U[now];tmp;tmp=E[tmp].nxt){
        int u=E[tmp].point;
        if(vis[u]){
            lca[tmp]=ancestor[find(u)];
            lca[tmp^1]=lca[tmp];
            int deepx=E[tmp].beginisU?deep[now]:deep[u];
            if(deepx-deep[lca[tmp]]==look[lca[tmp]])ans[lca[tmp]]--;
        } 
    }   
}
void dfs(int now){
    int rem1=sum[deep[now]+look[now]][0],rem2=sum[look[now]-deep[now]+Move][1];
    vis[now]=true;
    if(man[now]){
        for(int tmp=U[now];tmp;tmp=E[tmp].nxt){
            if(E[tmp].beginisU){
                sum[deep[now]][0]++;
                makesign(now,lca[tmp],-1);
            }       
            else{
                int len=deep[E[tmp].point]+deep[now]-2*deep[lca[tmp]];
                sum[len-deep[now]+Move][1]++;
                makesign(now,lca[tmp],len);
            }
        }
    }
    for(int tmp=head[now];tmp;tmp=nxt[tmp])
    {
        int u=point[tmp];
        if(!vis[u])
        {
            dfs(u);
            ans[now]+=sum[deep[now]+look[now]][0]-rem1+sum[look[now]-deep[now]+Move][1]-rem2;
            for(int tmp=sign[now];tmp;tmp=lis[tmp].nxt)
            {
                int v=lis[tmp].point;if(haha[tmp])break;
                if(lis[tmp].len<0)
                    sum[deep[v]][0]--;
                else
                    sum[lis[tmp].len-deep[v]+Move][1]--;    
                haha[tmp]=true;
            }
            rem1=sum[deep[now]+look[now]][0],rem2=sum[look[now]-deep[now]+Move][1];
        }
    }
    ans[now]+=sum[deep[now]+look[now]][0]-rem1+sum[look[now]-deep[now]+Move][1]-rem2;

}
int main(){
    int size=64<<20;
    char *p=(char*)malloc(size)+size;
    __asm__("movl %0, %%esp\n"::"r"(p)); 
    freopen("runninga.in","r",stdin);
    freopen("runninga.out","w",stdout);
    scanf("%d%d",&n,&m);int u,v,s,t;
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
    }
    for(int i=1;i<=n;i++) scanf("%d",&look[i]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&s,&t);
        if(s==t){
            if(look[s]==0)ans[s]++;
            continue;
        }
        man[s]=man[t]=true;
        Add(s,t);
    }
    Tar(1,0);
    memset(vis,0,sizeof(vis));
    dfs(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值