题解 P5327 [ZJOI2019]语言
竟然没有轻重链剖分+序列差分扫描线+虚树的做法。
枚举每个点能到达的点。发现就是包含这个点所有的
因为这些链都经过同一点,那么他们的并自然就是一棵连通子图(树)了。
先考虑如何求出包含该点的链。我们利用轻重链剖分将链划为
所以现在的任务是:
维护点集,支持插入、删除点(就是链的两端点
有趣的是,点数
我们将点按照 dfn 排序,看作一个环。答案就是所有相邻点之间的距离之和的一半。
这个可以用 set
轻重链剖分共造成了
#include<bits/stdc++.h>
#define x first
#define y second
#define ll long long
using namespace std;
struct node
{
int x,nxt;
}a[200005];
int h[100005];
int cnt;
int size[100005],f[100005],d[100005],son[100005];
int top[100005],dfn[100005];
int dcnt;
vector<pair<int,bool> > q[100005];
pair<int,int> c[100005];
void add(int x,int y)
{
a[++cnt].x=y;
a[cnt].nxt=h[x];
h[x]=cnt;
}
void dfs1(int x,int deep,int fa)
{
size[x]=1,f[x]=fa,d[x]=deep;
for(int i=h[x],y;i;i=a[i].nxt)
if((y=a[i].x)!=fa)
{
dfs1(y,deep+1,x);
size[x]+=size[y];
if(size[son[x]]<=size[y])
son[x]=y;
}
}
void dfs2(int x,int t)
{
top[x]=t;
dfn[x]=++dcnt;
if(!son[x]) return;
dfs2(son[x],t);
for(int i=h[x],y;i;i=a[i].nxt)
{
y=a[i].x;
if(y==f[x]||y==son[x]) continue;
dfs2(y,y);
}
}
void add_ar(int l,int r,int x)
{
q[l].push_back({x,1});
q[r+1].push_back({x,0});
}
void add(int x,int y,int id)
{
while(top[x]!=top[y])
{
if(d[top[x]]<d[top[y]]) swap(x,y);
add_ar(dfn[top[x]],dfn[x],id);
x=f[top[x]];
}
if(d[x]>d[y]) swap(x,y);
add_ar(dfn[x],dfn[y],id);
}
int asklca(int x,int y)
{
while(top[x]!=top[y])
{
if(d[top[x]]<d[top[y]]) swap(x,y);
x=f[top[x]];
//cout<<x<<endl;
}
return d[x]<d[y]?x:y;
}
int asklen(int x,int y)
{
return (!x||!y)?0:d[x]+d[y]-d[asklca(x,y)]*2;
}
struct node_set
{
set<pair<int,int> > t;
multiset<int> f;
int sum;
node_set()
{
t.insert({-2e9,0});
t.insert({2e9,0});
}
int val(int x)
{
int w1=(--t.lower_bound({dfn[x],x}))->second,w2=t.upper_bound({dfn[x],x})->second;
return asklen(w1,x)+asklen(w2,x)-asklen(w1,w2);
}
void insert(int x)
{
//cout<<"insert "<<x<<endl;
if(f.find(x)==f.end())
sum+=val(x),t.insert({dfn[x],x});
f.insert(x);
}
void erase(int x)
{
//cout<<"erase "<<x<<endl;
f.erase(f.find(x));
if(f.find(x)==f.end())
sum-=val(x),t.erase({dfn[x],x});
}
int ask()
{
return (sum+asklen((++t.begin())->second,(--(--t.end()))->second))/2;
}
}t;
int main()
{
int n,m,x,y,i,j;
ll sum=0;
scanf("%d%d",&n,&m);
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs1(1,1,0),dfs2(1,1);
for(i=1;i<=m;i++)
{
scanf("%d%d",&c[i].x,&c[i].y);
add(c[i].x,c[i].y,i);
}
for(i=1;i<=n;i++)
{
for(j=0;j<q[i].size();j++)
if(q[i][j].second)
t.insert(c[q[i][j].first].x),
t.insert(c[q[i][j].first].y);
else
t.erase(c[q[i][j].first].x),
t.erase(c[q[i][j].first].y);
sum+=t.ask();
}
cout<<sum/2;
}