1. 程式人生 > >「AHOI2008」「LuoguP4281」緊急集合 / 聚會(LCA

「AHOI2008」「LuoguP4281」緊急集合 / 聚會(LCA

距離 好玩 left 正整數 聯系 code pen tchar through

題目描述

歡樂島上有個非常好玩的遊戲,叫做“緊急集合”。在島上分散有N個等待點,有N-1條道路連接著它們,每一條道路都連接某兩個等待點,且通過這些道路可以走遍所有的等待點,通過道路從一個點到另一個點要花費一個遊戲幣。

參加遊戲的人三人一組,開始的時候,所有人員均任意分散在各個等待點上(每個點同時允許多個人等待),每個人均帶有足夠多的遊戲幣(用於支付使用道路的花費)、地圖(標明等待點之間道路連接的情況)以及對話機(用於和同組的成員聯系)。當集合號吹響後,每組成員之間迅速聯系,了解到自己組所有成員所在的等待點後,迅速在N個等待點中確定一個集結點,組內所有成員將在該集合點集合,集合所用花費最少的組將是遊戲的贏家。

小可可和他的朋友邀請你一起參加這個遊戲,由你來選擇集合點,聰明的你能夠完成這個任務,幫助小可可贏得遊戲嗎?

輸入輸出格式

輸入格式:

第一行兩個正整數N和M(N<=500000,M<=500000),之間用一個空格隔開。分別表示等待點的個數(等待點也從1到N進行編號)和獲獎所需要完成集合的次數。 隨後有N-1行,每行用兩個正整數A和B,之間用一個空格隔開,表示編號為A和編號為B的等待點之間有一條路。 接著還有M行,每行用三個正整數表示某次集合前小可可、小可可的朋友以及你所在等待點的編號。

輸出格式:

一共有M行,每行兩個數P,C,用一個空格隔開。其中第i行表示第i次集合點選擇在編號為P的等待點,集合總共的花費是C個遊戲幣。

輸入輸出樣例

輸入樣例#1: 復制
6 4  
1 2  
2 3  
2 4 
4 5
5 6
4 5 6
6 3 1
2 4 4 
6 6 6
輸出樣例#1: 復制
5 2
2 5
4 1
6 0


說明

提示:

40%的數據中N<=2000,M<=2000
100%的數據中,N<=500000,M<=500000


題解

對於每次詢問,設三個點分別為x1,x2,x3。

兩兩求lca,得到三個lca,其中最深的那個點為最優集合點。

不會嚴謹證明,畫畫圖感性理解還是不難的。

然後有了集合點,問題轉化為求樹上兩點間的距離。

隨便搞搞就出來了。

  1 /*
  2     qwerta
  3     P4281 [AHOI2008]緊急集合 / 聚會
  4     Accepted
  5     100
  6     代碼 C++,2.24KB
  7     提交時間 2018-10-09 18:50:02
  8     耗時/內存
  9     1148ms, 24100KB
 10 */
 11 #include<iostream>
 12 #include<cstdio>
 13 using namespace std;
 14 inline int read()
 15 {
 16     char ch=getchar();
 17     int x=0;
 18     while(!isdigit(ch))ch=getchar();
 19     while(isdigit(ch)){x=x*10+ch-0;ch=getchar();}
 20     return x;
 21 }
 22 const int MAXN=500003;
 23 struct emm{
 24     int e,f;
 25 }a[2*MAXN];
 26 int h[MAXN];
 27 int fa[MAXN],top[MAXN],d[MAXN],siz[MAXN],z[MAXN];
 28 void dfs(int x)
 29 {
 30     siz[x]=1,top[x]=x;
 31     int mac=0,macc=0;
 32     for(int i=h[x];i;i=a[i].f)
 33     if(!d[a[i].e])
 34     {
 35         d[a[i].e]=d[x]+1;
 36         fa[a[i].e]=x;
 37         dfs(a[i].e);
 38         siz[x]+=siz[a[i].e];
 39         if(siz[a[i].e]>macc){mac=a[i].e,macc=siz[a[i].e];}
 40     }
 41     z[x]=mac,top[mac]=x;
 42     return;
 43 }
 44 int q[MAXN],dfn[MAXN];
 45 int tot=0;
 46 void dfss(int x)
 47 {
 48     q[++tot]=x;
 49     dfn[x]=tot;
 50     if(z[x])dfss(z[x]);
 51     for(int i=h[x];i;i=a[i].f)
 52     if(fa[a[i].e]==x&&a[i].e!=z[x])
 53     dfss(a[i].e);
 54     return;
 55 }
 56 int fitop(int x)
 57 {
 58     if(top[x]==x)return x;
 59     return top[x]=fitop(top[x]);
 60 }
 61 int lca(int u,int v)//樹剖求lca
 62 {
 63     while(top[u]!=top[v])
 64     {
 65         if(d[top[u]]<d[top[v]])swap(u,v);
 66         u=fa[top[u]];
 67     }
 68     if(d[u]<d[v])swap(u,v);
 69     return v;
 70 }
 71 int dmin(int x1,int x2,int x3)//找深度最深的點
 72 {
 73     if(d[x1]>=d[x2]&&d[x1]>=d[x3])return x1;
 74     if(d[x2]>=d[x1]&&d[x2]>=d[x3])return x2;
 75     return x3;
 76 }
 77 int dis(int u,int v)//求距離
 78 {
 79     int ans=0;
 80     while(top[u]!=top[v])
 81     {
 82         if(d[top[u]]<d[top[v]])swap(u,v);
 83         ans+=dfn[u]-dfn[top[u]]+1;
 84         u=fa[top[u]];
 85     }
 86     if(d[u]<d[v])swap(u,v);
 87     ans+=dfn[u]-dfn[v];
 88     return ans;
 89 }
 90 void write(int x)
 91 {
 92     if(x>9)write(x/10);
 93     putchar(x%10+0);
 94     return;
 95 }
 96 int main()
 97 {
 98     //freopen("a.in","r",stdin);
 99     int n=read(),m=read();
100     tot=0;
101     for(int i=1;i<n;++i)
102     {
103         int u=read(),v=read();
104         a[++tot].f=h[u];
105         h[u]=tot;
106         a[tot].e=v;
107         a[++tot].f=h[v];
108         h[v]=tot;
109         a[tot].e=u;
110     }
111     int s=min(n,7);//幸運數賽高!(逃
112     d[s]=1;
113     dfs(s);
114     tot=0;
115     dfss(s);
116     for(int i=1;i<=n;++i)
117     top[i]=fitop(i);
118     for(int i=1;i<=m;++i)
119     {
120         int x1=read(),x2=read(),x3=read();
121         int l1=lca(x1,x2),l2=lca(x2,x3),l3=lca(x1,x3);//兩兩取lca
122         int p=dmin(l1,l2,l3);//取最深的為集合點
123         int ans=dis(x1,p)+dis(x2,p)+dis(x3,p);//算距離,加起來
124         write(p);
125         putchar( );
126         write(ans);
127         putchar(\n);//輸出
128     }
129     return 0;
130 }

「AHOI2008」「LuoguP4281」緊急集合 / 聚會(LCA