题目描述
欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有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个游戏币。
输入输出样例
说明
提示:
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(' ');//输出 128 } 129 return 0; 130 }