NKOJ-3773 緊急集合

由於沒有辦法複製過來,而我又很懶 所以只能大家自己去找了

題解

這道題目其實有點偏結論

首先 兩個人的話 他們最終集中的點肯定在這兩個人之間的路徑上

那麼這個時候再引入第三個人

將樣例畫出來 稍微變通一下就會發現
假如你多跑兩次LCA的話
最終的結果的兩倍就是三條路徑長度之和

另外還有幾個結論

最終集合的點一定是三個人兩兩公共祖先中的一個(這個應該不需要證明)
且這個點就是三個公共祖先中不同的那個點

小小地證明一下

每個點在集合時 肯定是要經過和其它的點的公共祖先的
當我們選擇相同的那個點作爲時
    就會有兩個人經過不同的那個點最後到達相同的那個點
    (因爲有兩條路徑是經過了那個點的,不同的點走過的路徑要單獨看)
反之 就只有一個點經過相同的點病到達不同的點

不同的點和相同的點之間的路徑是相同的
所以 相比之下 後者更優

附上對拍代碼

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int n,m,resa,resb,x,y,z,maxf,fl;
int all=0,star[500123],nxt[1000123],ent[1000123];
int dep[500123],up[500123][20];

inline int input()
{
    char c=getchar();int o;
    while(c>57||c<48)c=getchar();
    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
    return o;
}

void add(int s,int e)
{
    nxt[++all]=star[s];
    star[s]=all;
    ent[all]=e;
}

void search(int p,int f,int fa)
{
    dep[p]=f;
    up[p][0]=fa;
    for(int i=1;i<=18;i++)up[p][i]=up[up[p][i-1]][i-1];
    for(int bian=star[p],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])
        if(e!=fa)search(e,f+1,p);
}

int LCA(int a,int b)
{
    if(dep[a]<dep[b])swap(a,b);
    fl=dep[a]-dep[b];
    for(int i=0;i<=18;i++)
        if(fl&(1<<i))a=up[a][i];
    if(a==b)return a;
    for(int c=18;c>=0;c--)
        if(up[a][c]!=up[b][c])a=up[a][c],b=up[b][c];
    return up[a][0];
}

int getd(int p)
{
    return dep[p]+dep[resa]-dep[LCA(p,resa)]*2;
}

void mini(int fa,int fb,int fc)
{
    if(fa==fb)resa=fc;
    if(fb==fc)resa=fa;
    if(fa==fc)resa=fb;
    resb=getd(x)+getd(y)+getd(z);
}

int main()
{
//  freopen("In.txt","r",stdin);
    int ca,cb,cc,s,e;
    n=input();m=input();
    for(int i=1;i<n;i++)
    {
        s=input();e=input();
        add(s,e);add(e,s);
    }
    search(1,1,0);
    for(int i=1;i<=m;i++)
    {
        x=input();y=input();z=input();
        ca=LCA(x,y);cb=LCA(x,z);cc=LCA(y,z);
        mini(ca,cb,cc);
        printf("%d %d\n",resa,resb);
    }
}
發佈了79 篇原創文章 · 獲贊 15 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章