传送门
这题完全可以考虑暴力艹标算。
不过还是
s
t
O
O
(
n
log
n
)
stO\ O(n\log n)
stO O(nlogn)的标算。
假设当前处理的路径是
(
u
,
v
)
(u,v)
(u,v),对应的点的编号为
(
a
1
,
a
2
,
.
.
.
,
a
k
)
(a_1,a_2,...,a_k)
(a1,a2,...,ak),那么可以想成把
k
2
k^2
k2个小矩形
(
a
i
,
a
j
)
(a_i,a_j)
(ai,aj)全部覆盖。
于是最后只用询问被覆盖的矩形数,即矩形面积并,扫描线即可。
这样总复杂度是
O
(
n
∗
log
n
∑
i
=
1
m
p
a
t
h
_
l
e
n
g
t
h
i
2
)
O(n*\log n\sum_{i=1}^mpath\_length_i^2)
O(n∗logn∑i=1mpath_lengthi2)。
然后如果我们能使路径上点的编号变成连续的
s
s
s段,那么每次只需要拆成
s
2
s^2
s2个矩形。
于是不难想到用重链剖分将复杂度优化到
O
(
n
log
3
n
)
O(n\log^3n)
O(nlog3n).
然后你就
T
L
E
TLE
TLE成了
60
p
t
s
60pts
60pts因为吉司机把你树剖的两个
l
o
g
log
log给卡满啦!
于是我们考虑如何卡常过题。
首先把线段树换成
z
k
w
zkw
zkw线段树来卡常。
然后每次我们只询问全局被覆盖的点数,这样统计出来是一个无序点对。
A
n
s
=
(
总
点
对
数
+
未
被
覆
盖
的
(
i
,
i
)
的
数
量
−
n
)
/
2
Ans=(总点对数+未被覆盖的(i,i)的数量-n)/2
Ans=(总点对数+未被覆盖的(i,i)的数量−n)/2
那个未被覆盖的
(
i
,
i
)
(i,i)
(i,i)可以树上差分一波。
然后就能过啦!
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
typedef pair<int,int> pii;
const int N=1e5+5;
typedef long long ll;
int n,m,siz[N],hson[N],dep[N],top[N],fa[N],num[N],tot=0,cnt[N];
vector<int>e[N];
void dfs1(int p){
siz[p]=1;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
if(siz[v]>siz[hson[p]])hson[p]=v;
}
}
void dfs2(int p,int tp){
num[p]=++tot,top[p]=tp;
if(!hson[p])return;
dfs2(hson[p],tp);
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa[p]&&v!=hson[p])dfs2(v,v);
}
vector<pii>ad[N],dc[N];
inline void insert(int x1,int y1,int x2,int y2){
ad[x1].push_back(pii(y1,y2));
dc[x2+1].push_back(pii(y1,y2));
}
inline void modify(int x,int y){
static pii t[N];
int tp=0;
++cnt[x],++cnt[y];
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
t[++tp]=pii(num[top[x]],num[x]);
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
--cnt[y],--cnt[fa[y]];
t[++tp]=pii(num[y],num[x]);
for(ri i=1;i<=tp;++i)for(ri j=1;j<=tp;++j)insert(t[i].fi,t[j].fi,t[i].se,t[j].se);
}
int ql,qr;
namespace sgt{
#define lc (p<<1)
#define rc (p<<1|1)
int len[N<<3],cov[N<<3],ss[N<<3],m;
inline void build(){
for(m=1;m<=n;m<<=1);
for(ri i=1;i<=n;++i)len[m+i]=1;
for(ri p=m-1;p;--p)len[p]=len[lc]+len[rc];
}
inline void pushup(int p){ss[p]=cov[p]?len[p]:ss[lc]+ss[rc];}
inline void update(int v){
for(ql+=m-1,qr+=m+1;ql^qr^1;ql>>=1,qr>>=1){
if(ql&1^1)cov[ql^1]+=v,pushup(ql^1);
if(qr&1)cov[qr^1]+=v,pushup(qr^1);
pushup(ql),pushup(qr);
}
pushup(qr),qr>>=1;
while(ql)pushup(ql),ql>>=1;
}
#undef lc
#undef rc
#undef mid
}
ll ans=0;
void dfs(int p){
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa[p])dfs(v),cnt[p]+=cnt[v];
if(!cnt[p])++ans;
}
int main(){
n=read(),m=read();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
dfs1(n),dfs2(n,n);
for(ri i=1;i<=m;++i)modify(read(),read());
sgt::build();
dfs(n);
for(ri i=1;i<=n;++i){
for(ri j=0,up=ad[i].size();j<up;++j)ql=ad[i][j].fi,qr=ad[i][j].se,sgt::update(1);
for(ri j=0,up=dc[i].size();j<up;++j)ql=dc[i][j].fi,qr=dc[i][j].se,sgt::update(-1);
ans+=sgt::ss[1];
}
cout<<(ans-n)/2;
exit(0);
return 0;
}