云浅知处
2022-04-25 15:37:59
给定一棵
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 。
尽管这题可以有
而点分治可以处理边权任意的情形!考虑当前根为
对每个
其中
注意到
由于
前一项可以直接算,考虑后一项怎么算。
设
维护
感觉好像可以做到
#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;
}