[AHOI2008] 緊急集合
Description
歡樂島上有個非常好玩的遊戲,叫做“緊急集合”。在島上分散有N個等待點,有N-1條道路連接著它們,每一條道路都連接某兩個等待點,且通過這些道路可以走遍所有的等待點,通過道路從一個點到另一個點要花費一個遊戲幣。
參加遊戲的人三人一組,開始的時候,所有人員均任意分散在各個等待點上(每個點同時允許多個人等待),每個人均帶有足夠多的遊戲幣(用於支付使用道路的花費)、地圖(標明等待點之間道路連接的情況)以及對話機(用於和同組的成員聯系)。當集合號吹響後,每組成員之間迅速聯系,了解到自己組所有成員所在的等待點後,迅速在N個等待點中確定一個集結點,組內所有成員將在該集合點集合,集合所用花費最少的組將是遊戲的贏家。
小可可和他的朋友邀請你一起參加這個遊戲,由你來選擇集合點,聰明的你能夠完成這個任務,幫助小可可贏得遊戲嗎?
Input
第一行兩個正整數N和M(N<=500000,M<=500000),之間用一個空格隔開。分別表示等待點的個數(等待點也從1到N進行編號)和獲獎所需要完成集合的次數。 隨後有N-1行,每行用兩個正整數A和B,之間用一個空格隔開,表示編號為A和編號為B的等待點之間有一條路。 接著還有M行,每行用三個正整數表示某次集合前小可可、小可可的朋友以及你所在等待點的編號。
Output
一共有M行,每行兩個數P,C,用一個空格隔開。其中第i行表示第i次集合點選擇在編號為P的等待點,集合總共的花費是C個遊戲幣。
說明
\(100\%\)的數據中,\(N\leq500000\),\(M\leq500000\)
Solution
這題就差在說明裏寫上“這是一道性質題,推出來性質你就能A,不然就乖乖打暴力吧!”
首先能觀察到的是這三個點之間兩兩的 \(lca\) 只能是兩個點或更少
也就是說,必定有至少兩對點是同一個 \(lca\)
這啟發我們從 \(lca\) 入手推性質。
手玩幾組數據發現答案就是三個 \(lca\) 中深度較淺的那個。
所以直接求出這三個 \(lca\) 然後暴力求距離即可
但是正解好像是再推一下式子,發現無論如何 \[ans=dep[x]+dep[y]+dep[z]-dep[lca(x,y)]-dep[lca(x,z)]-dep[lca(y,z)]\]
求距離都不用,直接減就行了。
#include<cstdio>
#include<cctype>
#define N 500005
#define min(A,B) ((A)<(B)?(A):(B))
#define swap(A,B) ((A)^=(B)^=(A)^=(B))
int dfn[N],top[N],d[N];
int n,m,cnt,tot,sum[N<<2];
int fa[N],sze[N],son[N],head[N];
struct Edge{
int to,nxt;
}edge[N<<1];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
int getint(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
void first_dfs(int now){
sze[now]=1;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(sze[to])
continue;
fa[to]=now;
d[to]=d[now]+1;
first_dfs(to);
sze[now]+=sze[to];
if(sze[to]>sze[son[now]])
son[now]=to;
}
}
void second_dfs(int now,int low){
top[now]=low;
dfn[now]=++tot;
if(son[now])
second_dfs(son[now],low);
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(to==fa[now] or to==son[now])
continue;
second_dfs(to,to);
}
}
int query(int cur,int l,int r,int ql,int qr){
if(ql<=l and r<=qr)
return r-l+1;
int mid=l+r>>1,ans=0;
if(ql<=mid)
ans+=query(cur<<1,l,mid,ql,qr);
if(mid<qr)
ans+=query(cur<<1|1,mid+1,r,ql,qr);
return ans;
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]])
swap(x,y);
x=fa[top[x]];
}
if(d[x]<d[y])
swap(x,y);
return y;
}
int ask(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]])
swap(x,y);
ans+=query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(d[x]<d[y])
swap(x,y);
if(x!=y)
ans+=query(1,1,n,dfn[y]+1,dfn[x]);
return ans;
}
signed main(){
n=getint(),m=getint();
for(int i=1;i<n;i++){
int x=getint(),y=getint();
add(x,y);add(y,x);
}
d[1]=1; first_dfs(1);
second_dfs(1,1);
while(m--){
int a=getint(),b=getint(),c=getint();
int x=lca(a,b),y=lca(a,c),z=lca(b,c);
int ans=d[a]+d[b]+d[c]-d[x]-d[y]-d[z];
if(d[x]>=d[y] and d[x]>=d[z])
printf("%d %d\n",x,ans);
else if(d[y]>=d[x] and d[y]>=d[z])
printf("%d %d\n",y,ans);
else if(d[z]>=d[x] and d[z]>=d[y])
printf("%d %d\n",z,ans);
/*int a=getint(),b=getint(),c=getint();
int x=lca(a,b);
int y=lca(a,c);
if(x!=y){
if(d[x]<d[y]){
int ans=d[a]+d[c]-2*d[y];
ans+=ask(b,y);
printf("%d %d\n",y,ans);
} else{
int ans=d[a]+d[b]-2*d[x];
ans+=ask(c,x);
printf("%d %d\n",x,ans);
}
} else{
int z=lca(b,c);
if(z==x){
int ans=d[a]+d[b]+d[c]-3*d[z];
printf("%d %d\n",z,ans);
} else{
if(d[x]<d[z]){
int ans=d[b]+d[c]-2*d[z];
ans+=ask(a,z);
printf("%d %d\n",z,ans);
} else{
int ans=d[a]+d[b]-2*d[x];
ans+=ask(c,x);
printf("%d %d\n",x,ans);
}
}
}*/
}
return 0;
}
[AHOI2008] 緊急集合