题意
一棵 \(n\) 个节点的树, 树的每个节点上有一个观察员, 每一个观察员的观察时刻为 \(time_i\),
有 \(m\) 个玩家, 每个玩家在 \(0\) 时刻时从起点 \(s_i\) 开始跑步, 每时刻经过一个节点, 沿着最短路径向终点 \(t_i\) 跑去.
求每个观察员能观察到的玩家数量.
思路
暴力枚举每个玩家的行进路线一定是 \(O(n^2)\) 的, 不可行,
那么考虑枚举每一个观察员, 求它能观察到多少个选手.
若观察员 \(x\) 位于 \(s_i\) 到 \(lca_i\) 的路径上, 那么 \(x\) 能观察到 \(i\) 的条件为
\[ time_x = dep_{s_i} - dep_x \]
转化一下变为,
\[ time_x+dep_x = dep_{s_i} \]
若 \(x\) 位于 \(t_i\) 到 \(lca_i\) 的路径上, 那么 \(x\) 能观察到 \(i\) 的条件为
\[ time_x = dis_{s_i \to t_i} - (dep_t - dep_x) \]
转化,
\[ time_x - dep_x = dis - dep_t = dep_s - 2*dep_{lca_i} \]
看起来树上差分, 枚举每个点的子树, 开个桶记录就好了.
但有个问题, 就是之前的桶会影响当前枚举到的点.
我的做法比较蠢, 把差分的值从小到大排序, 然后每次树状数组查询.
其实我们考虑一下, 当前点需要的并不是整个桶, 只需要它自己的两个值上面的值就行了.
所以我们枚举到每个点的时候先记录一下它的两个值, 然后枚举完它的子树后, 再查看它的两个值, 求个差就行了.
代码
\(O(n\log n)\)
#include<bits/stdc++.h> #define pb push_back #define sz size using namespace std; const int N=299998+7; const int L=20; int n,m,dep[N],dfn[N],en[N],cnt,tme[N],f[N][L+7],ans[N],s[N],t[N],lca[N],qes[N],poi[N]; int t1[N],t2[N],c[N]; int lst[N],nxt[2*N],to[2*N],tot; vector<int> p[N]; void add(int x,int y){ nxt[++tot]=lst[x]; to[tot]=y; lst[x]=tot; } void pre(int u,int fa){ dep[u]=dep[fa]+1; dfn[u]=++cnt; f[u][0]=fa; for(int i=1;i<=L;i++) f[u][i]=f[f[u][i-1]][i-1]; for(int i=lst[u];i;i=nxt[i]){ int v=to[i]; if(v==fa) continue; pre(v,u); } en[u]=cnt; } int Lca(int x,int y){ if(dep[x]<dep[y]) swap(x,y); for(int i=L;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i]; if(x==y) return x; for(int i=L;i>=0;i--) if(f[x][i]!=f[y][i]){ x=f[x][i]; y=f[y][i]; } return f[x][0]; } bool rulea(int a,int b){ return tme[a]+dep[a]<tme[b]+dep[b]; } bool ruleb(int a,int b){ return tme[a]-dep[a]<tme[b]-dep[b]; } bool rule1(int a,int b){ return t1[a]<t1[b]; } bool rule2(int a,int b){ return t2[a]<t2[b]; } void modify(int x,int w){ if(!x) return; for(int i=x;i<=n;i+=i&-i){ c[i]+=w; }} int sum(int x){ if(!x) return 0; int res=0; for(int i=x;i;i-=i&-i) res+=c[i]; return res; } int query(int l,int r){ return sum(r)-sum(l-1); } int main(){ //freopen("run1.in","r",stdin); //freopen("run.out","w",stdout); cin>>n>>m; int x,y; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } pre(1,0); for(int i=1;i<=n;i++){ qes[i]=i; scanf("%d",&tme[i]); } for(int i=1;i<=m;i++){ scanf("%d%d",&s[i],&t[i]); lca[i]=Lca(s[i],t[i]); poi[i]=i; t1[i]=dep[s[i]]; t2[i]=dep[s[i]]-2*dep[lca[i]]; } sort(qes+1,qes+1+n,rulea); sort(poi+1,poi+1+m,rule1); int now=1; for(int i=1;i<=n;i++){ int tmp=tme[qes[i]]+dep[qes[i]]; while(now<=m&&t1[poi[now]]<tmp) now++; int lt=now; for(;now<=m&&t1[poi[now]]==tmp;now++){ modify(dfn[s[poi[now]]],1); modify(dfn[f[lca[poi[now]]][0]],-1); } while(i<=n&&tme[qes[i]]+dep[qes[i]]==tmp){ ans[qes[i]]+=query(dfn[qes[i]],en[qes[i]]); i++; } i--; for(;lt<now;lt++){ modify(dfn[s[poi[lt]]],-1); modify(dfn[f[lca[poi[lt]]][0]],1); } } sort(qes+1,qes+1+n,ruleb); sort(poi+1,poi+1+n,rule2); now=1; for(int i=1;i<=n;i++){ int tmp=tme[qes[i]]-dep[qes[i]]; while(now<=m&&t2[poi[now]]<tmp) now++; int lt=now; for(;now<=m&&t2[poi[now]]==tmp;now++){ modify(dfn[t[poi[now]]],1); modify(dfn[lca[poi[now]]],-1); } while(i<=n&&tme[qes[i]]-dep[qes[i]]==tmp){ ans[qes[i]]+=query(dfn[qes[i]],en[qes[i]]); i++; } i--; for(;lt<now;lt++){ modify(dfn[t[poi[lt]]],-1); modify(dfn[lca[poi[lt]]],1); } } for(int i=1;i<=n;i++) printf("%d ",ans[i]); putchar('\n'); return 0; }
\(O(n)\)
有时间再补吧.