题解 LGP1600【[NOIP2016 提高组] 天天爱跑步】
problem
一棵树,树上 \(m\) 个人从 \(s_i\) 跑到 \(t_i\),速度 \(v=1m/s\),求对于每个点在 \(w_i\) 时刻有多少个人刚好经过。\(n,m\leq 10^5\)。
solution 0
考虑一个人如果从 \(u\) 出发往上走,走到 \(k\) 时,时间是 \(dep_u-dep_k\)。
考虑从某个时刻 \(T\) 开始时间倒流,从 \(v\) 倒着往上走,走到 \(k\) 时,时间是 \(T-(dep_v-dep_k)=T-dep_v+dep_k\)。
移项:\(dep_u=w+dep_k,T-dep_v=w-dep_k\)。
树上差分之后线段树合并一下,复杂度 \(O(n\log n)\)。
solution 1
题解做法很 NB。
查询子树信息是吧,我在放入子树信息之前先记录现在的外面的子树信息,放完之后再查询一次,做差分就是真实的子树信息。
形式化的:
- dfs
- 在往下 dfs 之前查询一下记为 \(ans_1\)。
- dfs 完所有子树之后,查询当前答案记为 \(ans_2\)。
- 那么 \(ans=ans_2-ans_1\)。
复杂度 \(O(n)\)(LCA 当作 \(O(1)\))
code 0
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<int N,int M,class T=int> struct graph{
int head[N+10],nxt[M*2+10],cnt;
struct edge{
int u,v;T w;
edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
} e[M*2+10];
graph(){memset(head,cnt=0,sizeof head);}
edge&operator[](int i){return e[i];}
void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N,int M,class T=int> struct treecut: public graph<N,M,T>{
graph<N,M,T> &g=*this;
int fa[N+10],dep[N+10],son[N+10],siz[N+10],
dfn[N+10],rnk[N+10],top[N+10],cnt;
treecut(){memset(son,cnt=siz[0]=0,sizeof son);}
void dfs(int u,int f=0){
dep[u]=dep[fa[u]=f]+1,siz[u]=1;
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa[u]) continue;
dfs(v,u),siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void cut(int u,int topf){
top[rnk[dfn[u]=++cnt]=u]=topf;
if(son[u]) cut(son[u],topf);
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa[u]||v==son[u]) continue;
cut(v,v);
}
}
int lca(int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]) if(dep[top[u]]<dep[top[v]]) swap(u,v);
if(dep[u]<dep[v]) swap(u,v); return v;
}
int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
};
template<int N> struct segtree{
//单点加,单点查询,魔怔了
int ch[N+10][2],tot,ans[N+10];
int newnode(){int p=++tot;return ch[p][0]=ch[p][1]=ans[p]=0,p;}
segtree():tot(-1){newnode();}
void modify(int x,int k,int &p,int l=-3e5,int r=3e5){
if(!p) p=newnode();
if(l==r) return (void)(ans[p]+=k);
int mid=(l+r)>>1;
if(x<=mid) modify(x,k,ch[p][0],l,mid);
else modify(x,k,ch[p][1],mid+1,r);
ans[p]=ans[ch[p][0]]+ans[ch[p][1]];
}
int query(int x,int p,int l=-3e5,int r=3e5){
if(!p||x<l||r<x) return 0;
if(l==r) return ans[p];
int mid=(l+r)>>1;
return x<=mid?query(x,ch[p][0],l,mid):query(x,ch[p][1],mid+1,r);
}
void merge(int &p,int q,int l=-3e5,int r=3e5){
if(!p||!q) return (void)(p|=q);
if(l==r) return (void)(ans[p]+=ans[q]);
int mid=(l+r)>>1;
merge(ch[p][0],ch[q][0],l,mid);
merge(ch[p][1],ch[q][1],mid+1,r);
ans[p]=ans[ch[p][0]]+ans[ch[p][1]];
}
void dfs(int p,int l=-3e5,int r=3e5){
if(!p) return ;
if(l==r){
for(int i=1;i<=ans[p];i++) debug("%d ",l);
return ;
}
int mid=(l+r)>>1;
dfs(ch[p][0],l,mid),dfs(ch[p][1],mid+1,r);
}
};
int n,m,w[300010],ret[300010],root[300010],que[300010][2];
treecut<300010,300010> g;
segtree<300010*20> t;
void dfs(int u,int fa,int sgn){
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa) continue;
dfs(v,u,sgn),t.merge(root[u],root[v]);
}
ret[u]+=t.query(w[u]+sgn*g.dep[u],root[u]);
// debug("segtree %d:",u),t.dfs(root[u]),puts("");
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.link(u,v);
g.dfs(1),g.cut(1,1);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=m;i++) scanf("%d%d",&que[i][0],&que[i][1]);
memset(root,t.tot=0,sizeof root);
for(int i=1;i<=m;i++){
int u=que[i][0],v=que[i][1],T=g.dist(u,v);
t.modify(T-g.dep[v],1,root[v]),t.modify(T-g.dep[v],-1,root[g.lca(u,v)]);
}
dfs(1,0,-1);
memset(root,t.tot=0,sizeof root);
for(int i=1;i<=m;i++){
int u=que[i][0],v=que[i][1];
t.modify(g.dep[u],1,root[u]),t.modify(g.dep[u],-1,root[g.fa[g.lca(u,v)]]);
}
dfs(1,0,1);
for(int i=1;i<=n;i++) printf("%d%c",ret[i]," \n"[i==n]);
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-P1600.html