[NOIp2016提高组]天天爱跑步

题目大意:
  有一棵n个点的树,每个点上有一个摄像头会在第w[i]秒拍照。
  有m个人再树上跑,第i个人沿着s[i]到t[i]的路径跑,每秒钟跑一条边。
  跑到t[i]的下一秒,人就会消失。
  问每个摄像头会拍下几个人。

思路:
  首先很显然是要求LCA的。
  求完LCA怎么办?
  我们可以用树上差分的方法分别维护向上、向下的链。
  每一条路径,我们可以在s,t,lca,par[lca]上分别打标记。
  s +dep[s]
  t +dep[t]-len
  lca -dep[s]
  par[lca] +len-dep[t]
  其中有一些是向上走的链、有一些是向下走的链,因此我们还需要将它们区分开来。
  一个点x满足条件当且仅当x在s到lca的路上且dep[x]-dep[s]=w[x],
  或者x在lca到t的路上且dep[lca]-dep[s]+deo[lca]-dep[x]=w[x]。
  最后从根节点DFS统计一下,访问到x结点就把对应的标记加进去,ans[x]=统计完子树后的答案-统计之前的答案。
  另外注意dep[t]-len可能会是负的,因此我们可以将它们整体往右移一些位置。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<vector>
 4 inline int getint() {
 5     register char ch;
 6     while(!isdigit(ch=getchar()));
 7     register int x=ch^‘0‘;
 8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^‘0‘);
 9     return x;
10 }
11 const int N=299999,logN=19;
12 std::vector<int> e[N];
13 inline void add_edge(const int &u,const int &v) {
14     e[u].push_back(v);
15     e[v].push_back(u);
16 }
17 int w[N],dep[N],anc[N][logN];
18 inline int log2(const float &x) {
19     return ((unsigned&)x>>23&255)-127;
20 }
21 void dfs(const int &x,const int &par) {
22     dep[x]=dep[par]+1;
23     anc[x][0]=par;
24     for(register int i=1;i<=log2(dep[x]);i++) {
25         anc[x][i]=anc[anc[x][i-1]][i-1];
26     }
27     for(register unsigned i=0;i<e[x].size();i++) {
28         const int &y=e[x][i];
29         if(y==par) continue;
30         dfs(y,x);
31     }
32 }
33 inline int lca(int x,int y) {
34     if(dep[x]<dep[y]) std::swap(x,y);
35     for(register int i=log2(dep[x]-dep[y]);i>=0;i--) {
36         if(dep[anc[x][i]]>=dep[y]) {
37             x=anc[x][i];
38         }
39     }
40     if(x==y) return x;
41     for(register int i=log2(dep[x]);i>=0;i--) {
42         if(anc[x][i]!=anc[y][i]) {
43             x=anc[x][i];
44             y=anc[y][i];
45         }
46     }
47     return anc[x][0];
48 }
49 struct Tag {
50     bool type;
51     int val,sgn;
52 };
53 std::vector<Tag> tag[N];
54 int ans[N];
55 int cnt1[N<<2],cnt2[N<<2];
56 void stat(const int &x) {
57     const int tmp=cnt1[dep[x]+w[x]]+cnt2[dep[x]-w[x]+(N<<1)];
58     for(register unsigned i=0;i<tag[x].size();i++) {
59         const Tag &t=tag[x][i];
60         (t.type?cnt1:cnt2)[t.val]+=t.sgn;
61     }
62     for(unsigned i=0;i<e[x].size();i++) {
63         const int &y=e[x][i];
64         if(y==anc[x][0]) continue;
65         stat(y);
66     }
67     ans[x]=cnt1[dep[x]+w[x]]+cnt2[dep[x]-w[x]+(N<<1)]-tmp;
68 }
69 int main() {
70     const int n=getint(),m=getint();
71     for(register int i=1;i<n;i++) {
72         add_edge(getint(),getint());
73     }
74     for(register int i=1;i<=n;i++) {
75         w[i]=getint();
76     }
77     dfs(1,0);
78     for(register int i=0;i<m;i++) {
79         const int s=getint(),t=getint(),p=lca(s,t),len=dep[s]+dep[t]-(dep[p]<<1);
80         tag[s].push_back((Tag){1,dep[s],1});
81         tag[t].push_back((Tag){0,dep[t]-len+(N<<1),1});
82         tag[p].push_back((Tag){1,dep[s],-1});
83         tag[anc[p][0]].push_back((Tag){0,dep[t]-len+(N<<1),-1});
84     }
85     stat(1);
86     for(register int i=1;i<n;i++) {
87         printf("%d ",ans[i]);
88     }
89     printf("%d",ans[n]);
90     return 0;
91 }
时间: 2024-08-19 09:12:43

[NOIp2016提高组]天天爱跑步的相关文章

【NOIP2016提高组】愤怒的小鸟(状压宽搜)

题目描述 Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如的曲线,其中a,b是Kiana指定的参数,且必须满足a<0. 当小鸟落回地面(即x轴)时,它就会瞬间消失. 在游戏的某个关卡里,平面的第一象限中有n只绿色的小猪,其中第i只小猪所在的坐标为(xi,yi). 如果某只小鸟的飞行轨迹经过了(xi,yi),那么第i只小猪就会被消灭掉,同时小鸟将会沿着原先

noip2016——提高组——蚯蚓

大概这题难度提高+省选-. 我也做了半天. 这题如果用优先队列做的话会时间超限. 所以就要用另一种巧妙的做法. 见代码-- 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int read(){ 6 int t=1,num=0;char c=getchar(); 7 while(c>'9'||c<'0'){if(c=='-')t=-1

[NOIP2016提高组]组合数问题

题目:UOJ#263.洛谷P2822.Vijos P2006.codevs5947. 题目大意:t组数据,每次给你n和m$\leq 2000$,求对于所有的$(0\leq i\leq n)$,$(0\leq j\leq m)$的(i,j),有多少对满足$C^j_i\equiv 0(mod\ k)$. 解题思路:此题是一道数论题.首先,组合数有一个递推公式:$C^m_n=C^m_{n-1}+C^{m-1}_n$,这其实和杨辉三角的递推公式是一样的.那么我们可以预处理出所有的组合数,然后对于每一个问

JZOJ 4639 Angel Beats!【NOIP2016提高组A组7.16】

Angel Beats! (这是一部日漫,7.16的出题人好神奇,名字都来自于影音作品) 题目大意 给你一棵1为根的树,然后会有q个询问,向你查询点x子树和点y子树的重心,重心可能会有很多个,你只需要输出距离和即可. 两棵子树的重心的定义如下:在树上找到一个点,使得该点到两棵子树中所有点距离之和最小,即这两棵子树的重心. 输入格式 第一行一个整数 ,代表点的数量. 接下来 n-1行,第i 行的表示节点i 的父亲节点. 接下来一个整数q ,为询问的个数. 接下来q 行,每行两个数x,y ,表示查询

JZOJ 4638 第三条跑道 【NOIP2016提高组A组7.16】

第三条跑道 该题目的名字是一首歌 题目大意 输入格式 输出格式 对于每个询问,单独一行输出答案. 样例输入 5 2 3 4 5 6 3 1 1 5 0 2 3 6 1 2 3 样例输出 32 48 数据范围 题解 我们先看一下φ的通式. 其中p1, p2--pn为x的所有质因数,x是不为0的整数. 再看一下数据范围,,这也就意味着ai在任何时刻都满足它的素因子是600以内的,而φ(ai)只跟它的素因子有关. 因为是区间查询/修改,当然是开线段树了. 600以内的素因子有109个,所以我们就种10

洛谷 P1850 换教室(NOIp2016提高组D1T3)

题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 2n2n 节课程安排在 nn 个时间段上.在第 ii(1 \leq i \leq n1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 c_ic?i?? 上课,而另一节课程在教室 d_id?i?? 进行. 在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 nn 节安排好的课程.如果学生想更换第 ii 节课程的教室,则需要提出申请.

洛谷 P2827 蚯蚓(NOIp2016提高组D2T2)

题目描述 本题中,我们将用符号?c?表示对c向下取整,例如:?3.0?=?3.1?=?3.9?=3. 蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓. 蛐蛐国里现在共有n只蚯蚓(n为正整数).每只蚯蚓拥有长度,我们设第i只蚯蚓的长度为a_i(i=1,2,...,n)a?i??(i=1,2,...,n),并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓). 每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两

[NOIP2016提高组]愤怒的小鸟

题目:UOJ#265.洛谷P2831.Vijos P2008. 题目大意:有n头猪,都在一个二维坐标系里(每头猪坐标为两位小数).规定每只鸟能从(0,0)处发射,且经过的抛物线一定为$y=ax^2+bx$,且$a<0$. 如果几头猪头猪在同一条抛物线上,那么它就能被一只鸟打死.问至少发射多少只鸟才能打死所有的猪? 解题思路:由于n最大才18,我们可以用二进制的每一个位来保存一只鸟,做一个状压DP. 那么就是判断抛物线的事了.我们枚举两头猪,算出a和b的值.由于鸟从原点飞出,我们直接套公式计算即可

【NOIP2016提高组】换教室

https://www.luogu.org/problem/show?pid=1850 题面很长,实质很水的一道期望DP题.题面自带劝退效果. 首先用Floyd算出任意两点的最短路径.然后设f(i,j,0)为前i节课申请更换j节,且不申请第i节时的最小期望:设f(i,j,1)前i节课申请更换j节,且申请第i节时的最小期望.可得下面这个超长的状转方程:f(i,j,0)=min{ f(i-1,j,0) + dist(c[i-1],c[i]),    // 不申请第i-1节 f(i-1,j,1) +