NOIP模拟 相遇(LCA+树状数组+dfs序)
【题目描述】
豪哥生活在一个n个点的树形城市里面,每一天都要走来走去。虽然走的是比较的多,但是豪哥在这个城市里面的朋友并不是很多。
当某一天,猴哥给他展现了一下大佬风范之后,豪哥决定要获得一些交往机会来提升交往能力。豪哥现在已经物色上了一条友,打算和它(豪哥并不让吃瓜群众知道性别)交往。豪哥现在spy了一下这个人的所有行程起点和终点,豪哥打算从终点开始走到起点与其相遇。但是豪哥是想找话题的,他想知道以前有多少次行程和此次行程是有交集的,这样豪哥就可以搭上话了。这个路径与之前路径的有交集数量作为豪哥此次的交往机会。
但是豪哥急着要做交往准备,所以算什么交往机会的小事情就交给你了。
【输入格式】
第一行一个正整数n表示节点个数。接下来n-1行,每行两个正整数分别是u,v表示节点u和v之间有连边。接下来一行一个 正整数m表示路径个数。然后有m行,每行两个正整数分别是u,v分别表示u到v之间有一条路径。
【输出格式】
输出共m行,每行一个整数,第i行表示豪哥在这条路径上获得的交往机会。
【样例输入】
5
1 2
1 3
3 4
3 5
4
4 5
4 2
1 3
1 2
【样例输出】
0
1
2
2
【备注】
对于20%的数据n,m≤2000
对于另外20%的数据n,m≤50000
对于另外10%的数据n,m≤200000保证树形结构是一条链
对于另外50%的数据n,m≤200000
【题目分析】
(口胡开始)
我们先找一下对于两条路径(a,b)和(c,d),如果他们有交集有什么规律?
不难发现,如果两条路径有交集,那么对于lca(a,b)和lca(c,d),要么lca(a,b)在路径(c,d)上,要么lca(c,d)在(a,b)上,要么两个lca相同,证明就一句话,感性理解:假设我们现在手上是一条 lca 深度大的路径,这条路径最高可达点就是 lca,若 lca不在另一条路径上,又因为他lca深度大,所以不可能在另外一条路径 lca上方,所以就没有可达点了。
所以这个问题就转化为两个问题:1.当前路径上有几个之前的lca;2.之前路径有几个经过当前路径的lca;(当然要特判一下lca相同的时候)
1.当前路径上找lca
因为这是一颗静态树,所以考虑用dfs序来解决。对于一个点x,a->lca(a,b)这样的树链如果经过他,则一定是从它的子树走向它的祖先。 然后就比较显然了,每多加一个这样的x,则给他子树中所有点的权值都+1. 查询(a,lca(a,b),b)的时候,就用 a权+b权-lca权*2. 若 a与lca在x的上下两方,则会被累加到贡献。若同时在x的下方,则会被 lca权减掉。
2.在之前路径上找lca
同样地考虑dfs序, 很方便的树上前缀和。若有一条路径(a,b),则给lca(a,b) +1,a-1,b-1.然后查询一个点到根的权值和,这是离线时的套路。 但这题这样不好做,因为不能每一次都重构树,于是我们变一下,变成类似树上后缀和的东西。
对于一条路径(a,b),给a+1,b+1,lca-2. 一个点的子树权值和就是他的权值。
用两树状数组维护一下dfs 序上的信息,我们就把这道题给搞定了。
这题的坑点主要在于,两个树状数组维护的信息意义是不一样的,压根不可以相提并论。
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
#define lowbit(x) ((x)&(-x))
#define treesum(t,x) (sum(t,r[x])-sum(t,l[x]-1))
int n,m,stm,cnt;
int head[MAXN],depth[MAXN];
int l[MAXN],r[MAXN];
int to[MAXN<<1],nxt[MAXN<<1];
int f[MAXN][19];
int t1[MAXN],t2[MAXN],ans;
int app[MAXN];
void change(int *tr,int x,int v)
{
for(;x<=n;x+=lowbit(x))
tr[x]+=v;
}
int sum(int *tr,int x)
{
int ret=0;
for(;x;x-=lowbit(x))
ret+=tr[x];
return ret;
}
void add(int x,int y)
{
cnt++;
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
}
void dfs(int u,int fa)
{
l[u]=++stm;
f[u][0]=fa;
for(int i=1;i<19;++i)
f[u][i]=f[f[u][i-1]][i-1];
depth[u]=depth[fa]+1;
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(v!=fa)
{
dfs(v,u);
}
}
r[u]=stm;
}
int lca(int x,int y)
{
if (depth[x]<depth[y])
swap(x,y);
for(int i=18;i>=0;i--)
if(depth[f[x][i]]>=depth[y])
x=f[x][i];
if(x==y)
return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void calcans(int x,int y,int lc)
{
ans+=treesum(t1,lc);
ans+=sum(t2,l[x])+sum(t2,l[y])-sum(t2,l[lc])*2;
}
int main()
{
memset(head,-1,sizeof(head));
memset(nxt,-1,sizeof(nxt));
scanf("%d",&n);
for(int i=1;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,-1);
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
int lc=lca(x,y);
ans=0;
calcans(x,y,lc);
printf("%d\n",ans+app[lc]);
app[lc]++;
change(t1,l[x],1);
change(t1,l[y],1);
change(t1,l[lc],-2);
change(t2,l[lc],1);
change(t2,r[lc]+1,-1);
}
return 0;
}