Rooted Subtrees 題解(LCA+思維)
阿新 • • 發佈:2021-08-20
題目連結
題目思路
看起來好難但是結論好簡單
如果\(r,q\)中間有\(k\)個點,那麼答案就是\(c(k,2)+n\)
題解是這麼說的
You can take any contiguous sub-segment of the path between r and p.
至於為什麼加\(n\) ,我感覺有點難說清楚
如果他們兩棵樹取的子數的節點不同時在中間的k個點中,那麼答案就是\(n\)個
求中間多少個點直接\(LCA\)即可
程式碼
卷也卷不過,躺又躺不平#include<bits/stdc++.h> #define fi first #define se second #define debug printf(" I am here\n"); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<ll,ll> pii; mt19937 rnd(time(0)); const ll INF=0x3f3f3f3f3f3f3f3f; const int maxn=2e5+5,inf=0x3f3f3f3f,mod=20071027; const double eps=1e-10; int n,q; int head[maxn],cnt; struct edge{ int to,next; }e[maxn<<1]; int lg[maxn],depth[maxn]; int fa[maxn][30]; void add(int u,int v){ e[++cnt]={v,head[u]}; head[u]=cnt; } void dfs(int son,int father)//把0作為最高點,且0的高度為0 { fa[son][0]=father; depth[son]=depth[father]+1; for(int i=1;i<=lg[depth[son]];i++) { fa[son][i]=fa[fa[son][i-1]][i-1];//這個轉移可以說是演算法的核心之一 //意思是son的2^i祖先等於son的2^(i-1)祖先的2^(i-1)祖先 //2^i = 2^(i-1) + 2^(i-1) } for(int i=head[son];i;i=e[i].next) { if(e[i].to!=father)//相當於往下搜尋 dfs(e[i].to,son); } } int LCA(int x, int y){ if(depth[x] < depth[y]) //用數學語言來說就是:不妨設x的深度 >= y的深度 swap(x, y); while(depth[x] > depth[y]) x = fa[x][lg[depth[x]-depth[y]] - 1]; //先跳到同一深度 if(x == y) //如果x是y的祖先,那他們的LCA肯定就是x了 return x; for(int k = lg[depth[x]] - 1; k >= 0; k--) //不斷向上跳(lg就是之前說的常數優化) 注意是從大到小跳 if(fa[x][k] != fa[y][k]) //因為我們要跳到它們LCA的下面一層,所以它們肯定不相等,如果不相等就跳過去。 x = fa[x][k], y = fa[y][k]; return fa[x][0]; //返回父節點 } signed main(){ scanf("%d%d",&n,&q); for(int i = 1; i <= n; ++i) //預先算出log2(i)+1的值,用的時候直接呼叫就可以了 lg[i] = lg[i-1] + (1 << lg[i-1] == i); //看不懂的可以手推一下 for(int i=1,u,v;i<=n-1;i++){ scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(1,0); for(int i=1,u,v;i<=q;i++){ scanf("%d%d",&u,&v); int fa=LCA(u,v); int k=depth[u]-depth[fa]+depth[v]-depth[fa]+1; printf("%lld\n",1ll*k*(k-1)/2+n); } return 0; }