1. 程式人生 > 其它 >洛谷 P6374 樹上詢問

洛谷 P6374 樹上詢問

技術標籤:LCA

洛 谷 P 6374 樹 上 詢 問 洛谷~~P6374 ~~樹上詢問 P6374

題目描述 :洛谷 P6374

思路:

  1. 暴力列舉每個點 i i i,判斷 L C A ( a , b ) LCA(a,b) LCA(a,b)是否= c c c。複雜度 O ( n ⋅ q ⋅ l o g 2 ( n ) ) O(n·q·log_2(n)) O(nqlog2(n)),用腳想想都知道過不了(能騙一分是一分
  2. 貌似可以用那幾個特殊資料小搞一波,估計比暴力多個10分吧。(一分也是分
  3. 說說正解:
    首先,我們假設結點1為樹根,敲一遍 D F S DFS
    DFS
    L C A LCA LCA的預處理
    設dp[x]為以x為根的子樹的節點數。
    總共有四種情況:
    1. c c c不在 a = > b a=>b a=>b的路徑上,此時答案為0.
    2. c c c a , b a,b a,b L C A LCA LCA,此時答案為c與其祖先們的節點。

    重點來襲

    1. c為a~LCA(a,b)路徑上的一點,此時 a n s w e r answer answer=dp[c]-dp[v] v表示v為a所在子樹的根節點(以c為樹根時)
    2. c為b~LCA(a,b)路徑上的一點,此時 a n s w e r answer answer=dp[c]-dp[v] v表示v為b所在子樹的根節點(以c為樹根時)

程式碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define r register
#define rep(i,x,y) for(r int i=x;i<=y;++i)
#define per(i,x,y) for(r int i=x;i>=y;--i)
using namespace std;
typedef long long ll; const int V=5*1e5+100; ll n,q,x,y,z,top; ll f[V][23],d[V],dp[V],head[V]; struct node { ll next,to; }e[V<<1]; void add(ll x,ll y) { e[++top]=node{head[x],y}; head[x]=top; } void dfs(ll x,ll y) { d[x]=d[y]+1; dp[x]=1; f[x][0]=y; for(r ll i=head[x];i;i=e[i].next) { ll v=e[i].to; if(v!=y) { dfs(v,x); dp[x]+=dp[v]; //子樹大小 } } } ll LCA(ll x,ll y) //倍增求LCA { if(d[x]>d[y]) swap(x,y); ll len=d[y]-d[x],k=20,t=1<<k; while(len) { if(len>=t) len-=t,y=f[y][k]; t>>=1,--k; } if(x==y) return x; per(k,20,0) if(f[x][k]!=f[y][k]) { x=f[x][k]; y=f[y][k]; } return f[x][0]; } bool check(ll x,ll y, ll z) //判斷y是否在x-->z的路徑上 { ll k=LCA(x,z); return ((LCA(x,y)==y)||(LCA(y, z)==y))&&LCA(y,k)==k; } ll size(ll x,ll y) //找到點“v” { if(x==y) return 0; ll len=d[x]-d[y],k=20,t=1<<k; per(i,20,0) if(d[f[x][i]]>d[y]) x=f[x][i]; return dp[x]; } int main() { scanf("%lld%lld",&n,&q); rep(i,1,n-1) { scanf("%lld%lld",&x,&y); add(x,y),add(y,x); } dfs(1,0); rep(j,1,20) rep(i,1,n) f[i][j]=f[f[i][j-1]][j-1]; //LCA的預處理 rep(i,1,q) { scanf("%lld%lld%lld",&x,&y,&z); ll v=LCA(x,y); if(v==z) printf("%lld\n",n-size(x,v)-size(y,v)); else if(check(x,z,v)) printf("%lld\n",dp[z]-size(x,z)); else if(check(y,z,v)) printf("%lld\n",dp[z]-size(y,z)); else printf("0\n"); } return 0; }