传送门
解析:
考虑统计不合法的路径条数。
令 i n [ u ] in[u] in[u]表示 d f s dfs dfs序中, u u u的进入时间戳, o u t [ u ] out[u] out[u]表示退出时间戳。
一个方案可以表示为点集的笛卡尔积,由于是无向的,所以我们只考虑 i n [ u ] < i n [ v ] in[u] < in[v] in[u]<in[v]的路径。
一个限制可以表示为 ( a , k a ) (a,ka) (a,ka)的形式,即每条合法路径不能覆盖任何一个 ( a , k a ) (a,ka) (a,ka)。
所有限制显然一共只有 O ( n log n ) O(n\log n) O(nlogn)个,将所有限制全部提出来。
一个限制 ( u , v ) (u,v) (u,v)的实际影响可以分类讨论。假设 i n [ u ] < i n [ v ] in[u]<in[v] in[u]<in[v]
1.
u
u
u是
v
v
v的祖先。
则所有一端点在
v
v
v的子树内,另一端点在
u
u
u的子树外或
u
u
u不含
v
v
v的子树内部的路径全部都不合法。
设 u u u的儿子中,子树包含 v v v的为 t t t,则这个限制相当于ban掉了两部分的答案: [ 1 , i n [ t ] − 1 ] × [ i n [ v ] , o u t [ v ] ] [ i n [ v ] , o u t [ v ] ] × [ o u t [ v ] + 1 , n ] [1,in[t]-1] \times [in[v],out[v]] \\ [in[v],out[v]]\times[out[v]+1,n] [1,in[t]−1]×[in[v],out[v]][in[v],out[v]]×[out[v]+1,n]
2. u u u, v v v之间没有祖先后代关系
则端点分别在 u u u, v v v的子树中的路径不合法:
[ i n [ u ] , o u t [ u ] ] × [ i n [ v ] , o u t [ v ] ] [in[u],out[u]]\times[in[v],out[v]] [in[u],out[u]]×[in[v],out[v]]
我们发现所有限制可以表示成若干的矩形。
所有不合法的限制就是矩形的并集。
上线段树维护扫描线,计算矩形并集就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
using std::cout;
using std::cerr;
cs int N=1e5+5,logN=16;
int n;
std::vector<int> edge[N];
int in[N],out[N],tot;
int fa[N][18],dep[N];
void dfs(int u,int f){
in[u]=++tot;
for(int re i=0;fa[u][i];++i)fa[u][i+1]=fa[fa[u][i]][i];
for(int re e=0,v;e<edge[u].size();++e)if((v=edge[u][e])^f)fa[v][0]=u,dep[v]=dep[u]+1,dfs(v,u);
out[u]=tot;
}
inline bool isanc(int u,int v){
return in[u]<in[v]&&in[v]<=out[u];
}
inline int jumpto(int u,int v){
for(int re i=logN;~i;--i)
if(dep[fa[v][i]]>dep[u])v=fa[v][i];
return v;
}
struct node{int u,d,val;};
std::vector<node> vec[N];
inline void rectangle(int x_1,int x_2,int y_1,int y_2){
vec[x_1].push_back((node){y_2,y_1,1});
vec[x_2+1].push_back((node){y_2,y_1,-1});
}
inline void work(int u,int v){
if(in[u]>in[v])std::swap(u,v);
if(isanc(u,v)){
int son=jumpto(u,v);
rectangle(1,in[son]-1,in[v],out[v]);
rectangle(in[v],out[v],out[son]+1,n);
}
else {
rectangle(in[u],out[u],in[v],out[v]);
}
}
int full[N<<2],sum[N<<2];
inline int get(int k,int l,int r){
return full[k]?r-l+1:sum[k];
}
inline void modify(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){
if(ql<=l&&r<=qr){
full[k]+=val;
return ;
}
int mid=(l+r)>>1;
if(ql<=mid)modify(k<<1,l,mid,ql,qr,val);
if(mid<qr)modify(k<<1|1,mid+1,r,ql,qr,val);
sum[k]=get(k<<1,l,mid)+get(k<<1|1,mid+1,r);
}
ll ans;
signed main(){
n=getint();
for(int re i=1,u,v;i<n;++i){
u=getint(),v=getint();
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs(1,0);
for(int re i=1;(i<<1)<=n;++i)
for(int re j=i<<1;j<=n;j+=i)work(i,j);
ans=(ll)n*(n-1)>>1;
for(int re i=1;i<n;++i){
for(int re j=0;j<vec[i].size();++j)modify(1,1,n,vec[i][j].d,vec[i][j].u,vec[i][j].val);
ans-=get(1,1,n);
}
std::cout<<ans<<"\n";
return 0;
}