NOIP模拟 相遇
【题目描述】
豪哥生活在一个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; }