• 「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('
    ');//输出
    128     }
    129     return 0;
    130 }
  • 相关阅读:
    eclipse中常用快捷键
    js sort排序
    js parseInt函数
    Jquery常用方法
    jquery的call()和apply()方法
    Jquery中的事件命名机制
    CSS层叠样式表
    推荐博客园中好的博客主
    页面刷新或者子窗体刷新父窗体,不提示 "重试或取消”对话框
    FullCalendar日历插件使用说明
  • 原文地址:https://www.cnblogs.com/qwerta/p/9769506.html
Copyright © 2020-2023  润新知