OIIIIIIII

「AHOI2008」「LuoguP4281」紧急集合 / 聚会(LCA

题目描述

欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有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 }

 

posted @ 2018-10-10 22:14  qwertaya  阅读(252)  评论(0编辑  收藏  举报
MDZX
Changsha
Fulan