题目描述

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

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

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

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

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

输入输出格式

输入格式:

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

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

接下来一行 nnn个整数,其中第j" role="presentation">jjj个整数为WjWjW_j​ , 表示结点jjj出现观察员的时间。

接下来 m" role="presentation">mmm行,每行两个整数SiSiS_i​,和TiTiT_i​,表示一个玩家的起点和终点。

对于所有的数据,保证1≤Si,Ti≤n,0≤Wj≤n1≤Si,Ti≤n,0≤Wj≤n1≤Si,Ti≤n,0≤Wj≤n 。

输出格式:

输出1行 nnn个整数,第j" role="presentation">jjj个整数表示结点jjj的观察员可以观察到多少人。

输入输出样例

输入样例#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" role="presentation">111号点,Wi=0Wi=0W_i=0,故只有起点为111号点的玩家才会被观察到,所以玩家1" role="presentation">111和玩家222被观察到,共有2" role="presentation">222人被观察到。

对于222号点,没有玩家在第2" role="presentation">222秒时在此结点,共000人被观察到。

对于3" role="presentation">333号点,没有玩家在第555秒时在此结点,共0人被观察到。

对于4" role="presentation">444号点,玩家111被观察到,共1" role="presentation">111人被观察到。

对于555号点,玩家1" role="presentation">111被观察到,共111人被观察到。

对于6" role="presentation">666号点,玩家333被观察到,共1" role="presentation">111人被观察到。

数据范围

n≤3e5n≤3e5n \leq 3e5,m≤3e5m≤3e5m \leq 3e5

题目概要

给定一棵有n个结点的树,有m条有向路线,每条路线会对所有wiwiw_i=lenilenilen_i的点产生作用(wiwiw_i是点值,lenilenilen_i是该点离这条路线起点的距离),求每个点会被作用多少次

思路

这题暴力分挺多的哈,明显这题考试时就是让我们打暴力拼接程序拿部分分(大佬请略过)
部分分的性价比比正解高到不知道哪里去了

然而我去年这题把所有暴力打完后,想到一个近似正解的方法(现在想起来就是暴力剪枝),于是开始在暴力程序上直接修改,但一直没打出来,于是乎监考老师说要准备下考了,马上慌了,下定决心不继续打了,赶紧按Ctrl+zCtrl+zCtrl + z恢复之前的部分分程序(暴力分有808080啊),发现一个一个按有点慢,于是按住Ctrl+zCtrl+zCtrl + z不松手,马上系统就跳出一个窗口,现在不记得是什么了,只记得有一个OpsOpsOps,然后鬼知道我按了什么,程序就不动了,但感觉怪怪的,定睛一看,发现有点古怪

我的程序被拆了!!!

至于什么是被拆了,举个栗子:

下面是A+B的c++代码:

#include<bits/stdc++.h>
using namespace std;
int main(){int a,b;cin>>a>>b;cout<<a+b;return 0;
}

很美观是不? 然后经过那个鬼畜的电脑后就会变成:

#i;nd>b;cotdut<e
    cg nain>>a>/scnbclu< 0;
}e<bitstdin(){int a,++.h>
usinnt ma+b;returspace s;
ima

不要惊讶,就是这样,能感受到本蒟蒻考场上花俩小时打这题暴力(T3看不懂,当时本蒟蒻还不知道图论和期望,连Dp都没怎么写过),最后连编译都没过就交了的绝望吗?(估计这是OI史上最豪迈的一次丢分(lian)了)

有了这种绝望的感觉,就可以直面这题的疾风了

这题估计是NOIP从
National Olympiad in Informatics in Provinces
转变为
National Olympiad in Informatics Professional 或者是 National Olympiad in Informatics Plus
的罪魁祸首了吧

好了,吐槽能量已经满了,题解开始

对于这题,我们自然不能O(nm)O(nm)O(nm)暴力(只有25分),看看数据最后一个点(我们只追求正解,暴力大家都会打),3e53e53e5的数据范围,正解应该带logloglog或线性,那我们来看看到底哪些复杂度可以省掉

至于每一条路线,由于路线之间没有什么关联性,所以O(m)O(m)O(m)的复杂度是已经固定了的,我们只能在O(n)O(n)O(n)上动手

但是本蒟蒻智商有限,我们为什么不召唤神奇海螺呢?

于是,从神奇海螺的螺壳内传来一阵……

可以用LCA优化

%%%%%%%%%%

但并不能优化复杂度,m条从根到叶子结点的路线在链状树上会被卡

神奇海螺 says:

还可以利用差分O(1)修改啊

%%%%%%%%%%

dalao们一下就想到的正解,像我这种蒟蒻只能看着dalao AK虐场,自己打好自己的暴力

那么如何差分就成为了这题突破的关键

神奇海螺 says:

可以将线路上端+1,下端-1啊

%%%%%%%%%%

但是路线并不会严格地成上升或下降趋势,而通常情况是从一个结点到俩结点的LCA再到另一个结点
也就是 A−>Lca(A,B)−>BA−>Lca(A,B)−>BA->Lca(A,B)->B

神奇海螺 says:

可以把一条线路拆分为A到LCA和LCA到B啊

%%%%%%%%%%

但是如何维护差分呢?

神奇海螺 says:

因为假设一条线路为sss到t" role="presentation">ttt,depth[x]depth[x]depth[x]为xxx在树中的深度,len" role="presentation">lenlenlen为sss到t" role="presentation">ttt的路径长度,将sss到t" role="presentation">ttt的路径转化为从sss到lca" role="presentation">lcalcalca和lcalcalca到ttt,其次,因为题目要求求每一个点被影响的次数,所以我们采用桶来解决问题 //(桶的下标是当前结点的深度)

其中先考虑s" role="presentation">sss到lcalcalca的情况,对于一个点iii,只有当depth[s]=depth[i]+w[i]" role="presentation">depth[s]=depth[i]+w[i]depth[s]=depth[i]+w[i]depth[s]=depth[i]+w[i]时才会对点iii起效所以我们把depth[s]" role="presentation">depth[s]depth[s]depth[s]装在桶内,到时候在访问到每个点时将tong[depth[i]+w[i]]tong[depth[i]+w[i]]tong[depth[i]+w[i]]和tong[depth[i]−w[i]]tong[depth[i]−w[i]]tong[depth[i]-w[i]]的数量统计一下即可

同样地,考虑lcalcalca到ttt的情况,对于一个点i" role="presentation">iii,只有当depth[t]−len=depth[i]−w[i]depth[t]−len=depth[i]−w[i]depth[t]-len=depth[i]-w[i],放进桶中

看到这里,有一些像我一样的蒟蒻就会问:

桶不是线性的吗,如果把桶做成树状的,会在取tong[depth[t]−len]tong[depth[t]−len]tong[depth[t]-len]时取到lcalcalca的上级,进而对lcalcalca上级的上级产生影响,例如下图,其中444(s)" role="presentation">(s)(s)(s)到555(t)" role="presentation">(t)(t)(t)经过了333(lca)" role="presentation">(lca)(lca)(lca),但取tong[depth[t]−len]tong[depth[t]−len]tong[depth[t]-len]时取到了222(depth[t]−len)" role="presentation">(depth[t]−len)(depth[t]−len)(depth[t]-len)号点,然而这时若111号点w1=1" role="presentation">w1=1w1=1w_1=1,会将专门为555准备的2" role="presentation">222号桶调用,但实际上这是非法调用

万能的神奇海螺坐不住了

我们可以用一个专为向上(xia)(xia)(xia)(反过来是因为参考系是观察员)服务的桶和一个专为向下(shang)(shang)(shang)服务的桶,调用时只调用xia[depth[x]+w[i]]xia[depth[x]+w[i]]xia[depth[x]+w[i]]和shang[depth[x]−w[i]]shang[depth[x]−w[i]]shang[depth[x]-w[i]]

然而还有一个小小小小问题,就是这个桶在树型维护时由于桶的下标是深度,所以多个子树深度相同时可能有一点点bug,会互相干扰,所以我们在做树型差分时维护一个小小的差分,就是一搜到该点,就记录当前桶内与之相关的量,当遍历完它的子树时,要将当前桶内量减去当初记录的量,因为只有变化量才是当前子树可用的量,那么就完美解决这个问题了

不,还有一个小问题,就是depth[x]−w[i]depth[x]−w[i]depth[x]-w[i]可能会得到负数,所以若以此为下标访问桶……
加一个maxnmaxnmaxn就行了

至于差分的关键步骤,在代码中标出来了,具体步骤文字可能讲不太清楚实际上是博主语文太low了,就详见代码注释

#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define clm(x) memset(x,0x3f3f3f3f,sizeof(x))
#define rg registertemplate <typename _Tp> inline void read(_Tp &x){char c11=getchar();x=0;//读入优化压行保平安while(c11<'0'||c11>'9')c11=getchar();while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}return ;
}const int maxn=3e5,maxm=6e5;
int w[maxn];
struct node    {int v,nxt;} a[maxm],a1[maxm],a2[maxm],a3[maxm];
int head[maxn],head1[maxn],head2[maxn],head3[maxn];
int p=0,p1=0,p2=0,p3=0;
int shang[maxm<<1],xia[maxm<<1];
int ans[maxn],cnt[maxn];
int n,m;
int anc[maxn][23],depth[maxn];inline void add(int,int);
inline void add1(int u,int v){a1[++p1].v=v;a1[p1].nxt=head1[u];head1[u]=p1;}//这仨是用于差分的
inline void add2(int u,int v){a2[++p2].v=v;a2[p2].nxt=head2[u];head2[u]=p2;}//类似于链式前向星
inline void add3(int u,int v){a3[++p3].v=v;a3[p3].nxt=head3[u];head3[u]=p3;}//用于快速访问路线void init();int LCA(int,int);void lca_dfs(int,int);void pre();void dfs(int x,int las){    //最重要的函数int xia_=xia[depth[x]+w[x]],shang_=shang[depth[x]-w[x]+maxn];//标记刚遍历时的相关桶数据xia[depth[x]]+=cnt[x];                                      //更新xia桶,以x为底的路线数for(rg int i=head1[x];i;i=a1[i].nxt)++shang[a1[i].v+maxn];  //因为是向下覆盖做贡献,所以要先处理for(rg int i=head[x];i;i=a[i].nxt)if(a[i].v!=las)dfs(a[i].v,x);//深搜ans[x]=xia[depth[x]+w[x]]+shang[depth[x]-w[x]+maxn]-xia_-shang_;//该点所需数据已足,可以算答案了//注意是用差分来算for(rg int i=head2[x];i;i=a2[i].nxt){--xia[a2[i].v];if(a2[i].v==depth[x]+w[x])--ans[x];}//该点下面的点应消除对上点的差分影响//另外若发现有"shang"桶中含有的数据,要减一for(rg int i=head3[x];i;i=a3[i].nxt)--shang[a3[i].v+maxn];//删掉这条有lca引出的向上“虚链”
}void print(){for(rg int i=1;i<=n;++i)printf("%d ",ans[i]);printf("\n");}int main(){init();pre();dfs(1,0);print();return 0;
}void pre(){lca_dfs(1,0);for(rg int i=1;i<=20;i++)for(int j=1;j<=n;j++)anc[j][i]=anc[anc[j][i-1]][i-1];//用递归版LCA会挂,玄学操作,因为这个调了一下午,打了这么久的LCA居然挂了int u,v;for(rg int i=1;i<=m;++i){read(u);read(v);int lca=LCA(u,v),len=depth[u]+depth[v]-(depth[lca]<<1),over=depth[v]-len;//len是整条路径的长度,over是“虚链”的上端点++cnt[u];           //对以u为底的路线进行处理add1(v,over);add2(lca,depth[u]);add3(lca,over);//分别对应1,2,3号操作}
}int LCA(int x,int y){if(x==y)return x;if(depth[x]<depth[y])swap(x,y);for(rg int i=22;i>-1;--i)if(depth[anc[x][i]]>=depth[y])x=anc[x][i];if(x==y)return x;for(rg int i=22;i>-1;--i)if(anc[x][i]!=anc[y][i])x=anc[x][i],y=anc[y][i];return anc[x][0];
}void lca_dfs(int x,int las){                                  //LCA预处理int i;depth[x]=depth[las]+1,anc[x][0]=las;for(rg int i=head[x];i;i=a[i].nxt)if(a[i].v!=las)lca_dfs(a[i].v,x);
}void init(){read(n);read(m);cl(head);cl(head1);cl(head2);cl(head3);cl(cnt);cl(xia);cl(shang);cl(ans);int A,B;for(rg int i=1;i<n;++i){read(A);read(B);add(A,B);add(B,A);}for(rg int i=1;i<=n;++i)read(w[i]);
}inline void add(int u,int v){a[++p].v=v;a[p].nxt=head[u];head[u]=p;
}

【题解】NOIP-2016 天天爱跑步相关推荐

  1. NOIP 2016 天天爱跑步

    [题目分析] 分析一下每个人的路径,在树中可以分为上升的一段和下降的一段.那么对于上升的一段,它的深度+观察时间是一个定值的时候,才可能被观察到:对于下降的情况,它的观察时间减去深度是一个定值.所以我 ...

  2. [luogu]P1600 天天爱跑步[LCA]

    [luogu]P1600 [NOIP 2016]天天爱跑步 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上 ...

  3. NOIP 2016 提高组 Day 1 第二题 天天爱跑步

    题目描述 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一棵包 ...

  4. 【NOIP 2016】Day1 T2 天天爱跑步

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

  5. NOIP提高组2016 D1T2 【天天爱跑步】

    码了一个下午加一个晚上吧...... 题目描述: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成 ...

  6. NOIP2016天天爱跑步 题解报告【lca+树上统计(桶)】

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn个 ...

  7. Noip 2016 Day1 题解

    老师让我们刷历年真题, 然后漫不经心的说了一句:"你们就先做做noip2016 day1 吧" ...... 我还能说什么,,,,,老师你这是明摆着伤害我们啊2333333333 ...

  8. NOIP2016提高组 第一天第二题 天天爱跑步running 题解

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点 ...

  9. [NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)

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

最新文章

  1. react控制 input 框回车之后内容清空
  2. shell命令的文本计算
  3. TOPAS 命令详解
  4. python3.1.1_python 3.1.1 with--enable shared:将不会构建任何扩展
  5. hbase java admin_java连接hbase(一):Admin功能接口表管理
  6. Linux 查看磁盘空间实现代码介绍
  7. KCC创建复制拓扑失败-故障处理
  8. mysql 字符串搜_Mysql搜索字符串
  9. mysql创建学生表命令_用sql语句创建学生表如何做
  10. 网上书店软件需求说明书
  11. C#选择文件的对话框和选择文件夹的对话框
  12. Java中成员变量的超详解
  13. Netty8# Netty之ByteBuf初探
  14. 使用fastcoll生成字符串MD5碰撞
  15. 今天不忙,咱们来说说域名是什么意思?
  16. CTF-reverse菜鸡想要走出菜狗设计的迷宫
  17. 容迟网络中的路由算法学习笔记
  18. 机房收费系统总结之5——抽象工厂+反射+配置文件
  19. html视频教程全套
  20. 同济大学高等数学上册电子版_函数的凹凸性漫谈|高等数学漫步(二)

热门文章

  1. 在word 页眉插入章编号+标题
  2. unet预测图片全黑/全灰解决方案(keras)
  3. lap.lapjv函数cost_limit参数
  4. ModuleNotFoundError: No module named ‘lap‘
  5. pip install lap出现问题
  6. 解决caffe2最新编译问题,亲测有效!
  7. JAVA - base64图片加文字水印
  8. 鲁大师被win10识别为病毒?
  9. Python爬虫: 单网页 所有静态网页 动态网页爬取
  10. 简图记录-《君主论》阅读总结