【XSY4206】QWQ(trick)
两个问题的解决方法感觉很妙:
一、
给你若干棵树 \(T_1,T_2,\cdots,T_k\),设 \(f(T_i,u,v)\) 为树 \(T\) 中 \(lca(u,v)\) 的深度,问如何优美地表示 \(g(u,v)=\min_{i=1}^k f(T_i,u,v)\)。
其实很简单,设 \(P_u\) 为一个 \(k\) 元组序列,设第 \(d\) 位为 \((a_{d,1},a_{d,2},\cdots,a_{d,k})\),那么 \(a_{d,i}\) 就表示在树 \(T_i\) 中从根到 \(u\) 路径上深度为 \(d\) 的那个节点(若不存在就设为一个不可能和其他任何值相等的值)。然后把 \(P_1,\cdots,P_n\) 插入一棵 Trie 树中,那么 \(g(u,v)\) 就是 Trie 树中 \(P_u\) 和 \(P_v\) 的 lca 的深度(即 \(P_u\) 和 \(P_v\) 的最长公共前缀)。
那么直接维护 \(P_1,\cdots,P_n\) 这些节点在 Trie 树上的虚树即可。构建过程中需要用到比较 dfn、查询深度、查询 lca,这些都可以推出来。
二、
给你树上若干个关键点 \(p_1,\cdots,p_k\),保证 \(k\) 为偶数,要求将它们两两配对,使得每对点的距离之和最小。支持动态插入、删除关键点和查询。
首先按 dfn 序排序然后相邻两两配对的思路是错的,因为不同的递归儿子顺序会导致不同的 dfn 序,从而配对结果不同,不一定能找到最优解。
事实上答案等价于有多少条边两侧各有奇数个关键点,因为若全为奇数,则一定有至少一组配对若会经过这条边,而且能构造出来仅有一组配对会经过这条边;若全为偶数,则一定能构造出来不经过这条边的方案。(不可能一奇一偶,因为保证询问时 \(k\) 为偶数)
那么也就是问有多少个点的 \(size\) 是奇数,使用树剖维护即可。
原题代码:
#include<bits/stdc++.h>
#define LN 18
#define N 200010
#define ll long long
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n;
pii pos[N];
struct Tree
{
int cnt,head[N],nxt[N],to[N];
int idx,dfn[N],d[N],fa[N][LN];
void adde(int u,int v)
{
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u)
{
dfn[u]=++idx;
for(int i=1;i<=17;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa[u][0]) continue;
d[v]=d[u]+1,fa[v][0]=u;
dfs(v);
}
}
void init()
{
d[1]=1;
dfs(1);
}
inline int jump(int a,int dep)
{
if(d[a]==dep) return a;
for(int i=17;i>=0;i--)
if(d[fa[a][i]]>=dep)
a=fa[a][i];
return a;
}
inline int getlca(int a,int b)
{
if(d[a]<d[b]) swap(a,b);
a=jump(a,d[b]);
if(a==b) return a;
for(int i=17;i>=0;i--)
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
}t1,t2;
namespace Ftree
{
const int V=N<<1;
map<pii,int>mp;
int node;
int rt,idx,fa[V],d[V],size[V],son[V],top[V],id[V],rk[V];
int cnt,head[V],to[V],nxt[V],from[V];
void adde(pii a,pii b,int dis)
{
if(!mp[a]) mp[a]=++node;
if(!mp[b]) mp[b]=++node;
int u=mp[a],v=mp[b];
to[++cnt]=v;
from[v]=dis;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u)
{
size[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa[u]) continue;
d[v]=d[u]+1,fa[v]=u;
dfs(v);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs1(int u,int tp)
{
top[u]=tp;
rk[id[u]=++idx]=u;
if(son[u]) dfs1(son[u],tp);
for(int i=head[u];i;i=nxt[i])
if(to[i]!=fa[u]&&to[i]!=son[u])
dfs1(to[i],to[i]);
}
void init()
{
rt=mp[mk(1,1)];
dfs(rt),dfs1(rt,rt);
}
}
namespace Segment
{
int sum[N<<3][2];
bool lazy[N<<3];
void up(int k)
{
sum[k][0]=sum[k<<1][0]+sum[k<<1|1][0];
sum[k][1]=sum[k<<1][1]+sum[k<<1|1][1];
}
void downn(int k)
{
swap(sum[k][0],sum[k][1]);
lazy[k]^=1;
}
void down(int k)
{
if(lazy[k])
{
downn(k<<1),downn(k<<1|1);
lazy[k]=0;
}
}
void build(int k,int l,int r)
{
if(l==r)
{
sum[k][0]=Ftree::from[Ftree::rk[l]];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
up(k);
}
void update(int k,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
downn(k);
return;
}
down(k);
int mid=(l+r)>>1;
if(ql<=mid) update(k<<1,l,mid,ql,qr);
if(qr>mid) update(k<<1|1,mid+1,r,ql,qr);
up(k);
}
void init(){build(1,1,Ftree::node);}
}
namespace Build
{
inline bool cmpdfn(const pii &a,const pii &b)
{
const int d1=t1.d[t1.getlca(a.fi,b.fi)],d2=t2.d[t2.getlca(a.se,b.se)];
if(d1<=d2) return t1.dfn[a.fi]<t1.dfn[b.fi];
return t2.dfn[a.se]<t2.dfn[b.se];
}
inline pii getlca(const pii &a,const pii &b)
{
const int d1=t1.d[t1.getlca(a.fi,b.fi)],d2=t2.d[t2.getlca(a.se,b.se)];
const int mind=min(d1,d2);
return mk(t1.jump(a.fi,mind),t2.jump(a.se,mind));
}
inline int getd(const pii &now)
{
return t1.d[now.fi];
}
int top;
pii sta[N];
void insert(pii now)
{
if(!top)
{
sta[++top]=now;
return;
}
pii lca=getlca(sta[top],now);
while(top>1&&getd(lca)<=getd(sta[top-1]))
Ftree::adde(sta[top-1],sta[top],getd(sta[top])-getd(sta[top-1])),top--;
if(lca!=sta[top])
{
assert(getd(lca)!=getd(sta[top]));
Ftree::adde(lca,sta[top],getd(sta[top])-getd(lca));
sta[top]=lca;
}
sta[++top]=now;
}
void work()
{
static pii p[N];
for(int i=1;i<=n;i++)
{
int a=i,b=i;
if(t1.d[a]<t2.d[b]) b=t2.jump(b,t1.d[a]);
else a=t1.jump(a,t2.d[b]);
pos[i]=p[i]=mk(a,b);
}
sort(p+1,p+n+1,cmpdfn);
for(int i=1;i<=n;i++) insert(p[i]);
while(top>1)
Ftree::adde(sta[top-1],sta[top],getd(sta[top])-getd(sta[top-1])),top--;
}
}
void insert(int u)
{
using namespace Ftree;
using namespace Segment;
while(u)
{
update(1,1,node,id[top[u]],id[u]);
u=fa[top[u]];
}
}
int main()
{
// freopen("qwq4.in","r",stdin);
// freopen("qwq4_my.out","w",stdout);
n=read();
for(int i=2;i<=n;i++)
t1.adde(read(),i),t2.adde(read(),i);
t1.init(),t2.init();
Build::work();
Ftree::init();
Segment::init();
ll sumd=0;
for(int i=1;i<=n;i++)
{
int u=Ftree::mp[pos[i]];
sumd+=Build::getd(pos[i]);
insert(u);
if(!(i&1))
{
assert(!((sumd-Segment::sum[1][1])&1));
printf("%lld\n",(sumd-Segment::sum[1][1])/2);
}
}
return 0;
}