一、题目
二、解法
考虑 (f(l,r)) 的实际意义就是保留 ([l,r]) 中的点后树的直径。直径的合并是一个常见结论,但是还不足以解决这道题,这里我们要引入树上圆理论,可以去看看 cmd 的博客(我不想复读一遍)
考虑移动右端点,维护每个左端点对应的答案,虽然可能有单调性但并不好算出直径。其实这类涉及区间最值求和的问题可以考虑猫树分治,每次只处理过中点的 (f(l,r)),这时候我们移动左指针右指针就会有单调性:第一段是由左边决定的、第二段是共同决定的、第三段是由右边决定的(...)
我们把这个东西套上邻域理论,我们求出 ([l...mid]) 的圆记为 (A_l),求出 ((mid...r]) 的圆记为 (B_r):
- 如果 (A_l) 包含 (B_r),那么答案就是 (A_l) 的直径。
- 如果 (B_r) 包含 (A_l),那么答案就是 (B_r) 的直径。
- 否则答案是 (A_l) 的半径 (+) (B_r) 的半径 (+) 两个圆的圆心距。
所以拿两个指针维护即可,最难算的是圆心距,可以拆成深度表达式,那么只需要计算 ( t lca) 的深度和即可,发现这个就是套路的 LCA,写个 ( t bit) 维护树剖即可,时间复杂度 (O(nlog^3 n))
好难写啊,我调了一个晚上。
#include <cstdio>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int M = 200005;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,Ind,siz[M],sd[M],sr[M],fa[M][20];
int ans,b1[M],b2[M],top[M],num[M],son[M],dep[M];
vector<int> g[M];
//tree dividing
void dfs1(int u,int p)
{
dep[u]=dep[p]+1;
siz[u]=1;fa[u][0]=p;
for(int i=1;i<20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(auto v:g[u])
{
if(v==p) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;num[u]=++Ind;
if(son[u]) dfs2(son[u],tp);
for(auto v:g[u])
if(v^fa[u][0] && v^son[u])
dfs2(v,v);
}
//bit-array
int lowbit(int x)
{
return x&(-x);
}
void upd(int x,int f)
{
for(int i=x;i<=m;i+=lowbit(i))
b1[i]+=f,b2[i]+=(x-1)*f;
}
int ask(int x)
{
int r=0;
for(int i=x;i>=1;i-=lowbit(i))
r+=b1[i]*x,r-=b2[i];
return r;
}
void ins(int u,int f)
{
while(u)
{
upd(num[u]+1,-f);
upd(num[top[u]],f);
u=fa[top[u]][0];
}
}
int qry(int u)
{
int res=0;
while(u)
{
res+=ask(num[u]);
res-=ask(num[top[u]]-1);
u=fa[top[u]][0];
}
return res;
}
//find lca and get dis
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;i>=0;i--)
if(dep[fa[u][i]]>=dep[v])
u=fa[u][i];
if(u==v) return u;
for(int i=19;i>=0;i--)
if(fa[u][i]^fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int dis(int u,int v)
{
return dep[u]+dep[v]-2*dep[lca(u,v)];
}
int jump(int u,int x)
{
for(int i=19;i>=0;i--)
if(x&(1<<i)) u=fa[u][i];
return u;
}
//tree circle structure
struct node {int x,r;}s[M],q[M];
bool cmp(node a,node b)
{
return a.r<b.r;
}
int get(int u,int v,int x)
{
int p=lca(u,v);
if(dep[u]-dep[p]>=x) return jump(u,x);
return jump(v,dep[v]-dep[p]-(x-dep[u]+dep[p]));
}
node merge(node a,int b)
{
int d=dis(a.x,b);
if(a.r>=d) return a;//within
return node{get(a.x,b,(d-a.r)/2),(d+a.r)/2};
}
int in(node a,node b)//does b contains a yes->1
{
return a.r+dis(a.x,b.x)<=b.r;
}
void cdq(int l,int r)
{
if(l==r) return ;
int mid=(l+r)>>1;
cdq(l,mid);cdq(mid+1,r);
s[mid]=node{mid,0};sr[mid]=sd[mid]=0;
s[mid+1]=node{mid+1,0};sr[mid+1]=0;
//initialize for prefix & suffix
for(int i=mid-1;i>=l;i--)
s[i]=merge(s[i+1],i);
for(int i=mid+2;i<=r;i++)
{
s[i]=merge(s[i-1],i);
sr[i]=sr[i-1]+s[i].r;
}
for(int i=mid+1;i<=r;i++)
sd[i]=sd[i-1]+dep[s[i].x];
//cal l<=mid<=r
int p1=mid,p2=mid,k=0;
for(int i=mid;i>=l;i--)
{//(mid,p1] left , (p1,p2] both , (p2,r] right
while(p1<r && in(s[p1+1],s[i])) p1++;
while(p2<r && !in(s[i],s[p2+1])) p2++;
p2=max(p2,p1);
// assert(p1<=p2);
ans+=2*(p1-mid)*s[i].r+2*(sr[r]-sr[p2])
+(p2-p1)*dep[s[i].x]+(sd[p2]-sd[p1])
+(p2-p1)*s[i].r+(sr[p2]-sr[p1]);
if(p1<p2)
{
if(p1>mid) q[++k]=node{-s[i].x,p1};
q[++k]=node{s[i].x,p2};
}
}
sort(q+1,q+1+k,cmp);
for(int i=mid+1,j=1;i<=r;i++)
{
ins(s[i].x,1);
while(j<=k && q[j].r==i)
{
int x=q[j].x;
if(x<0) ans+=2*qry(-x);
else ans-=2*qry(x);
j++;
}
}
for(int i=mid+1;i<=r;i++) ins(s[i].x,-1);
}
signed main()
{
n=read();m=2*n-1;
for(int i=1;i<n;i++)
{
int u=read(),v=read();
g[i+n].push_back(v);
g[v].push_back(i+n);
g[u].push_back(i+n);
g[i+n].push_back(u);
}
dfs1(1,0);
dfs2(1,1);
cdq(1,n);
printf("%lld
",ans/2);
//assert(ans%2==0);
}