CF771C Bear and Tree Jumps

云浅知处

2022-04-25 15:37:59

Solution

> 给定一棵 $n$ 个节点的树与正整数 $k$,求 > $$ > \sum_{i=1}^n\sum_{j=1}^n\left\lceil\dfrac{\text{dist}(i,j)}{k}\right\rceil > $$ > 的值。$1\le n\le 2\times 10^5,1\le k\le 5$。 尽管这题可以有 $O(nk^2)$ 的 dp 做法,但是仅限于边权为 $1$ 的情况。 而点分治可以处理边权任意的情形!考虑当前根为 $\text{rt}$ 的时候怎么算。 对每个 $u$ 处理出来 $\text{rt}\to u$ 路径上的边权和,记作 $d_u$,那么要求的就是: $$ \sum_{b_u\neq b_v}\left\lceil\dfrac{d_u+d_v}{k}\right\rceil $$ 其中 $b_u$ 表示 $u$ 是从哪个子树过来的。我们要能快速算出这个东西就行了。 注意到 $k$ 很小,考虑将 $d_u$ 按照 $\bmod k$ 的余数分类。 由于 $d_u+d_v=k\lfloor d_u/k\rfloor+(d_u\bmod k)+k\lfloor d_v/k\rfloor +(d_v\bmod k)$,故 $$ \left\lceil\dfrac{d_u+d_v}{k}\right\rceil=\lfloor d_u/k\rfloor+\lfloor d_v/k\rfloor+\left\lceil\dfrac{(d_u\bmod k) +(d_v\bmod k)}{k}\right\rceil $$ 前一项可以直接算,考虑后一项怎么算。 设 $f_i$ 为当前子树内满足 $d_u\bmod k=i$ 的 $u$ 的个数,$g_i$ 为前面子树内 $\bmod k=i$ 的个数,那么后一项的和就是 $$ \sum_{i=0}^{k-1}\sum_{j=0}^{k-1}g_if_j\left\lceil\dfrac{i+j}{k}\right\rceil=\sum_{i=0}^{k-1}g_i\left(\sum_{j=1-i}^{k-i}f_j+2\sum_{j=k-i+1}^{k-1}f_j\right) $$ 维护 $f,g$ 以及 $f$ 的前缀和即可。$O(nk\log n)$。 感觉好像可以做到 $O((n+k)\log n)$ 之类的? ```cpp #include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;} for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15); return x*f; } const int MN=2e5+5; const int MK=10; int f[MK],g[MK],fs[MK],n,k; vector<int>G[MN]; int sz[MN],rt,all,mx[MN],ans=0; bool vis[MN]; void calcsiz(int u,int fa){ mx[u]=0,sz[u]=1; for(int v:G[u]){ if(vis[v]||v==fa)continue; calcsiz(v,u),sz[u]+=sz[v],mx[u]=max(mx[u],sz[v]); } mx[u]=max(mx[u],all-sz[u]); if(mx[u]<mx[rt]||rt==0)rt=u; } int now=0; void calcdis(int u,int fa,int de){ f[de%k]++,now+=(int)(de/k); for(int v:G[u]){ if(v==fa||vis[v])continue; calcdis(v,u,de+1); } } void work(int u){ memset(g,0,sizeof(g)),memset(f,0,sizeof(f)),memset(fs,0,sizeof(fs)); vis[u]=1,g[0]=1;int cnt=1,sum=0; for(int v:G[u]){ if(vis[v])continue; memset(f,0,sizeof(f)),now=0,calcdis(v,u,1); ans+=(now*cnt+sum*sz[v]),sum+=now,cnt+=sz[v]; fs[0]=f[0];for(int i=1;i<k;i++)fs[i]=fs[i-1]+f[i]; for(int i=0;i<k;i++){ if(i==0)ans+=g[i]*(fs[k-1]-fs[0]); else ans+=g[i]*(fs[k-i]+2*(fs[k-1]-fs[k-i])); } for(int i=0;i<k;i++)g[i]+=f[i]; } } void dfz(int u){ work(u); for(int v:G[u]){ if(vis[v])continue; rt=0,all=sz[v],calcsiz(v,u),calcsiz(rt,u),dfz(rt); } } signed main(void){ #ifndef ONLINE_JUDGE freopen("in.in","r",stdin); #endif n=read(),k=read(); for(int i=1;i<=n-1;i++){ int u=read(),v=read(); G[u].push_back(v),G[v].push_back(u); } all=n,calcsiz(1,0),calcsiz(rt,0),dfz(rt); cout<<ans<<endl; return 0; } ```