标签:cto nlog clu prim ble using n+1 ret nlogn
题目描述
给一棵 $n$ 个点的树,每条边的边权为 $1$ 。
强制在线。
有 $m$ 次询问,每次询问给出 $u^{\prime}, v^{\prime}, w^{\prime}$ ,令 $u=\left(u^{\prime}+\text {lastans}\right) \% n+1, v= \left(v^{\prime}+\text {lastans}\right) \% n+1, w=\left(w^{\prime}+\text {lastans}\right) \% n$ , $lastans$ 为上一次询问的答案,初始时 $lastans=0$ 。
求有多少个点 $x$ 满足, $\min (\operatorname{dis}(x, u), \operatorname{dis}(x, v)) \leq w, \quad \operatorname{dis}(x, y)$ 等于 $x,y$ 在树上的距离
数据范围
$1 \leq n, m \leq 10^{5}, 1 \leq u, v, u^{\prime}, v^{\prime} \leq n, 0 \leq w, w^{\prime}<n$
题解
考场不应该想不出的题。
考虑分别对于 $u,v$ 求出 $dis(u,x) \le w$ , $dis(v,x) \le w$ 的点数,动态点分即可。
没想出如何去重,其实还挺简单的,就是 $dis(x,mid) \le w-\frac{dis(u,v)}{2}$ ,其中 $mid$ 是 $u,v$ 的中点,画个图理解一下吧。这个也可以动态点分。
为了方便,将边化成点,距离就都是偶数。
效率: $O(nlogn) \sim O(nlog^2n)$ 。
代码
#include <bits/stdc++.h> using namespace std; const int N=2e5+5,M=N<<1; int rt,o,n,m,son[N],sz[N],V[M],nx[M],hd[N],top[N],H,ax[M]; int a[N],d[N],e[N],f[N],up[N],c,tt,T[M],ans,s[N],ac[N][20]; bool vis[N];vector<int>g[M]; void add(int u,int v){ V[++tt]=v;nx[tt]=hd[u];hd[u]=tt; } void dfs1(int x,int fa){ ac[x][0]=f[x]=fa;d[x]=d[fa]+1;sz[x]=1; for (int i=1;ac[ac[x][i-1]][i-1];i++) ac[x][i]=ac[ac[x][i-1]][i-1]; for (int i=hd[x];i;i=nx[i]) if (V[i]!=fa){ dfs1(V[i],x);sz[x]+=sz[V[i]]; if (sz[s[x]]<sz[V[i]]) s[x]=V[i]; } } void dfs2(int x,int tp){ top[x]=tp; if (s[x]) dfs2(s[x],tp); for (int i=hd[x];i;i=nx[i]) if (V[i]!=f[x] && V[i]!=s[x]) dfs2(V[i],V[i]); } int lca(int x,int y){ while(top[x]!=top[y]){ if (d[top[x]]<d[top[y]]) swap(x,y); x=f[top[x]]; } if (d[x]>d[y]) swap(x,y);return x; } int dis(int x,int y){ return d[x]+d[y]-(d[lca(x,y)]<<1); } void change(int x){ for (int v,i=x;up[i];i=up[i]) v=dis(x,up[i]), ax[up[i]]=max(ax[up[i]],v), ax[i+H]=max(ax[i+H],v); } void update(int x){ g[x][0]++; for (int v,i=x;up[i];i=up[i]) v=dis(x,up[i]),g[up[i]][v]++,g[i+H][v]++; } void getrt(int x,int fa){ sz[x]=1;son[x]=0; for (int i=hd[x];i;i=nx[i]) if (V[i]!=fa && !vis[V[i]]) getrt(V[i],x),sz[x]+=sz[V[i]], son[x]=max(son[x],sz[V[i]]); son[x]=max(son[x],o-sz[x]); if (son[x]<son[rt]) rt=x; } void work(int x,int fa){ vis[x]=1;up[x]=fa; for (int i=hd[x];i;i=nx[i]) if (!vis[V[i]]) o=sz[V[i]],rt=0, getrt(V[i],x),work(rt,x); } void go(int x,int y,int w){ ans+=w*g[x][min(y,ax[x])]; for (int v,i=x;up[i];i=up[i]){ v=dis(x,up[i]); if (v<=y) ans+=w*g[up[i]][min(y-v,ax[up[i]])], ans-=w*g[i+H][min(y-v,ax[i+H])]; } } int main(){ son[0]=1e9;scanf("%d%d",&n,&m);H=n; for (int x,y,i=1;i<n;i++) scanf("%d%d",&x,&y),H++, add(x,H),add(H,x),add(y,H),add(H,y); dfs1(1,0);dfs2(1,1);o=H; getrt(1,0);work(rt,0);tt=0; for (int i=1;i<=n;i++) change(i); for (int i=1;i<=H+H;i++) for (int j=0;j<=ax[i];j++) g[i].push_back(0); for (int i=1;i<=n;i++) update(i); for (int i=1;i<=H+H;i++) for (int j=1;j<=ax[i];j++) g[i][j]+=g[i][j-1]; for (int u,v,w,l,x,y;m--;){ scanf("%d%d%d",&u,&v,&w); u=(u+ans)%n+1;v=(v+ans)%n+1; w=(w+ans)%n*2;ans=0;go(u,w,1);go(v,w,1); l=lca(u,v);y=d[u]+d[v]-(d[l]<<1); if (d[u]>d[v]) swap(u,v);x=v; for (int i=19;~i;i--) if (d[ac[x][i]]>=d[l] && ((d[v]-d[ac[x][i]])<<1)<=y) x=ac[x][i]; y>>=1;if (w>=y) go(x,w-y,-1); printf("%d\n",ans); } return 0; }
标签:cto nlog clu prim ble using n+1 ret nlogn
原文地址:https://www.cnblogs.com/xjqxjq/p/11969889.html