[NOIP2016]天天爱跑步(树上差分+线段树合并)
将每个人跑步的路径拆分成x->lca,lca->y两条路径分别考虑:
对于在点i的观察点,这个人(s->t)能被观察到的充要条件为:
1.直向上的路径:w[i]=dep[s]-dep[i],移项得w[i]+dep[i]=dep[s]
2.直向下的路径:w[i]=dep[s]-dep[lca]+dep[i]-dep[lca],移项得w[i]-dep[i]=dep[s]-2*dep[lca]。
问题转化为,对每个点i,统计它的子树中有多少个点x满足dep[x]=w[i]+dep[i]或dep[x]-2*dep[lca]=w[i]-dep[i],这是经典的线段树合并问题。
注意到并不是子树中所有满足条件的点都能被统计,因为有的点还没到观察点就往下跑了(lca深度大于当前观察点),差分解决。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lson ls[x],L,mid
#define rson rs[x],mid+1,R
#define rep(i,l,r) for (int i=l; i<=r; i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,M=;
int n,m,u,v,cnt,s,t,w[N],d[N],ans[N],fa[N][],h[N],nxt[N<<],to[N<<];
void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } struct T{
int nd,v[M],ls[M],rs[M],rt[N];
void ins(int &x,int L,int R,int pos,int k){
if (!x) x=++nd;
if (L==R){ v[x]+=k; return; }
int mid=(L+R)>>;
if (pos<=mid) ins(lson,pos,k); else ins(rson,pos,k);
} int merge(int x,int y,int L,int R){
if (!x || !y) return x+y;
if (L==R) { v[x]+=v[y]; return x; }
int mid=(L+R)>>;
ls[x]=merge(ls[x],ls[y],L,mid);
rs[x]=merge(rs[x],rs[y],mid+,R);
return x;
} int que(int x,int L,int R,int pos){
if (!x) return ;
if (L==R) return v[x];
int mid=(L+R)>>;
if (pos<=mid) return que(lson,pos); else return que(rson,pos);
}
}T1,T2; void dfs(int x){
rep(i,,) fa[x][i]=fa[fa[x][i-]][i-];
For(i,x) if ((k=to[i])!=fa[x][]) fa[k][]=x,d[k]=d[x]+,dfs(k);
} void dfs2(int x){
For(i,x) if ((k=to[i])!=fa[x][]){
dfs2(k);
T1.rt[x]=T1.merge(T1.rt[x],T1.rt[k],,n);
T2.rt[x]=T2.merge(T2.rt[x],T2.rt[k],,*n);
}
ans[x]+=(w[x]+d[x]>= && w[x]+d[x]<=n) ? T1.que(T1.rt[x],,n,w[x]+d[x]) : ;
ans[x]+=(w[x]-d[x]>=-n && w[x]-d[x]<=n) ? T2.que(T2.rt[x],,*n,w[x]-d[x]+n) : ;
} int Lca(int x,int y){
if (d[x]<d[y]) swap(x,y);
int t=d[x]-d[y];
for (int i=; ~i; i--) if (t&(<<i)) x=fa[x][i];
if (x==y) return x;
for (int i=; ~i; i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][];
} int main(){
freopen("running.in","r",stdin);
freopen("running.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d%d",&u,&v),add(u,v),add(v,u);
rep(i,,n) scanf("%d",&w[i]);
dfs();
rep(i,,m){
scanf("%d%d",&s,&t); int lca=Lca(s,t);
T1.ins(T1.rt[s],,n,d[s],); T1.ins(T1.rt[fa[lca][]],,n,d[s],-);
T2.ins(T2.rt[t],,*n,d[s]-*d[lca]+n,);
T2.ins(T2.rt[fa[lca][]],,*n,d[s]-*d[lca]+n,-);
if (d[s]-d[lca]==w[lca]) ans[lca]--;
}
dfs2();
rep(i,,n) printf("%d ",ans[i]); puts("");
return ;
}
[NOIP2016]天天爱跑步(树上差分+线段树合并)的更多相关文章
- 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并
题目描述 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. 输入 第一行有两个整数N和M .其中N代 ...
- [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)
首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...
- [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)
[BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...
- LOJ #2359. 「NOIP2016」天天爱跑步(倍增+线段树合并)
题意 LOJ #2359. 「NOIP2016」天天爱跑步 题解 考虑把一个玩家的路径 \((x, y)\) 拆成两条,一条是 \(x\) 到 \(lca\) ( \(x, y\) 最近公共祖先) 的 ...
- 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)
传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...
- bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...
- BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)
题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...
- Luogu5327 ZJOI2019语言(树上差分+线段树合并)
暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...
- P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)
显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...
随机推荐
- Farey Sequence (欧拉函数+前缀和)
题目链接:http://poj.org/problem?id=2478 Description The Farey Sequence Fn for any integer n with n >= ...
- TOYS(计算几何基础+点与直线的位置关系)
题目链接:http://poj.org/problem?id=2318 题面: TOYS Time Limit: 2000MS Memory Limit: 65536K Total Submiss ...
- poj 2104 可持久化线段树
我们先离散化,然后根据权值建立线段树,假设我们现在有一颗权值线段树,表示在区间1-n中每个数出现了几次,那么我们可以二分的求出来这个区间的k大值,类似sbt的select操作,那么因为点的权值插入是无 ...
- bzoj 3453 数论
首先我们知道对于f(x)来说,它是一个k次的多项式,那么f(x)的通项公式可以表示成一个k+1次的式子,且因为f(x)没有常数项,所以我们设这个式子为 f(x)=Σ(a[i]*x^i) (1<= ...
- Linux 入门记录:五、vi、vim 编辑器
一.vi.vim编辑器 vi 是一个命令行界面下的文本编辑工具,最早在 1976 年由 Bill Joy 开发.当时名字叫做 ex.vi 支持绝大多数操作系统(最早在类 Unix 操作系统的 BSD上 ...
- Github精选 – 适合移动端的HTML5 Datepicker
2016-01-12 (updated: 2017-01-07) 15731 通过 HTML5 的 <input> 新属性可以简单方便地调用手机的原生 Datepicker,但功能较弱, ...
- jdbc操作数据库以及防止sql注入
public class pr { public static void main(String[] args) { Connection conn = null; Statement st = nu ...
- linux命令行任务管理
今天看到了linux命令行的任务管理命令感觉很实用: 1.ctrl+z 将当前前台执行的任务放到后台并暂停 2.fg恢复上次放入后台的任务 这两个命令组合起来很实用,比如在linux命令行中写pyt ...
- js刷新页面代码
第一种: location.reload() 第二种: location.replace(location.href) 第三种: history.go() 第四种: location=location ...
- [编程题] 合唱团 && 地闹逃脱
1. 合唱团 有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最 ...