hdu6115 lca+暴力

这个题当时比赛的时候算了下,lca+暴力会超时。后来去网上搜题解。他们说可以a 可能是数据不强吧。

#include<stdio.h>
#include<string.h>
#include<vector>
#include<stdlib.h>
#include<math.h>
#define max 100010
#define maxl 27
using namespace std;
typedef struct
{
    int from,to,w;
}edge;
//这个结构体用来存储边
vector<int>zz[max];
vector<edge>edges;
vector<int> G[max];
//保存边的数组
int grand[max][maxl],gw[max][maxl];//x向上跳2^i次方的节点,x到他上面祖先2^i次方
int depth[max];//深度
int in[max];
int n,m,N;
void addedge(int x,int y,int w)//把边保存起来的函数
{
   edge a={x,y,w},b={y,x,w};
   edges.push_back(a);
   edges.push_back(b);
   G[x].push_back(edges.size()-2);
   G[y].push_back(edges.size()-1);
}
void dfs(int x)//dfs建图
{
    for(int i=1;i<=N;i++)//第一个几点就全部都是0咯,第二个节点就有变化了,不理解

    {
        grand[x][i]=grand[grand[x][i-1]][i-1];
        gw[x][i]=gw[x][i-1]+gw[grand[x][i-1]][i-1];
       // if(grand[x][i]==0) break;
    }

    for(int i=0;i<G[x].size();i++)
    {   edge  e = edges[G[x][i]];
         if(e.to!=grand[x][0])//这里我们保存的是双向边所以与他相连的边不是他父

           {
                depth[e.to]=depth[x]+1;//他儿子的深度等于他爸爸的加1
                grand[e.to][0]=x;//与x相连那个节点的父亲等于x
                gw[e.to][0]=e.w;//与x相连那个节点的距离等于这条边的距离
                dfs(e.to);//深搜往下面建
           }
    }
}
void Init(){
    //n为节点个数
    N = floor(log(n + 0.0) / log(2.0));//最多能跳的2^i祖先
    //根结点的祖先不存在,用-1表示
    memset(grand,0,sizeof(grand));
    memset(gw,0,sizeof(gw));
    memset(depth,-1,sizeof(depth));
    for(int i=1;i<=n;i++)if(!in[i]){depth[i]=0;dfs(i); break;}//以1为根节点建树

}
int lca(int a,int b)
{ if(depth[a] > depth[b]) swap(a, b);//保证a在b上面,便于计算
    int ans = 0;
    for(int i = N; i >= 0; i--) //类似于二进制拆分,从大到小尝试
        if(depth[a] < depth[b] && depth[grand[b][i]] >= depth[a])//a在b下面且b向


            ans +=gw[b][i], b=grand[b][i];//先把深度较大的b往上跳
    for(int j=N;j>=0;j--)//在同一高度了,他们一起向上跳,跳他们不相同节点,当全都
    {
        if(grand[a][j]!=grand[b][j])
        {   ans+=gw[a][j];
            ans+=gw[b][j];
            a=grand[a][j];
            b=grand[b][j];

        }
    }
    if(a!=b)//a等于b的情况就是上面土色字体的那种情况
    {
        ans+=gw[a][0],ans+=gw[b][0];
        a=grand[a][0];
        b=grand[b][0];
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
  while( t--)
  {
      scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++)
      {
          G[i].clear();
      }
      for(int i=1;i<=m;i++)
      {
          zz[i].clear();
      }
      edges.clear();
      memset(in,0,sizeof(in));
      for(int i=1;i<n;i++)
      {
          int x,y,w;
          scanf("%d%d%d",&x,&y,&w);
          addedge(x,y,w);
          in[y]++;
      }
      Init();
      int x,y;
      for(int i=1;i<=m;i++)
         {
             scanf("%d",&x);
             for(int j=1;j<=x;j++)
             {
                 scanf("%d",&y);
                 zz[i].push_back(y);
             }
         }
      //   printf("%d\n",lca(1,1));
      int q;
      scanf("%d",&q);
      int s,e;
      for(int i=1;i<=q;i++)
      {
          scanf("%d %d",&s,&e);
          int min=999999999;
          for(int j=0;j<zz[s].size();j++)
          {
              for(int k=0;k<zz[e].size();k++)
              {
                  int mm=lca(zz[s][j],zz[e][k]);
                  if(mm<min) min=mm;
              }

          }
            printf("%d\n",min);
      }

  }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值