jzoj 5844.【省选模拟2018.8.23】c 倍增lca

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/81984660

Description
给定一个无向连通图, n 个点(下标从 1 开始), m 条边,每条边有一个颜色。保证无自环,没有长度超过 2 的简单环。
现有 q 个询问:给出两个点 x y ,选择一条 x y 简单路径(不经过重复的点),经过的边将形成一个颜色序列,价值为相同颜色的极大连续段个数,求出最大的价值。

Input
第一行,一个正整数 n ,一个自然数 m
接下来 m 行,每行三个正整数 u v w ,代表一条边 ( u , v ) ,颜色为 w 。接下来一行,一个自然数 q
接下来 q 行,每行两个正整数 x y ,代表一次询问。

Output
q 行,每行一个自然数,代表该询问的答案。

Data Constraint
这里写图片描述

分析:
好狗的一道题,一开始连题目都看错了,询问的是颜色段的个数而不是最长段的长度。
然后我打了一个300多行的链剖
首先因为没有自环和长度大于 2 的环,所以相当于树有许多重边。
显然我们要颜色尽量不同,我们考虑一条边颜色尽量和前面的不一样。考虑一条边的贡献,他只要记录下三种不同的颜色即可(因为一条边最多与两条边相邻,要选不同只需记录 3 条边)。
考虑倍增求,我们设 g [ i ] [ j ] [ x = 0 / 1 / 2 ] [ y = 0 / 1 / 2 ] 表示 i 向上走 2 j 步的路径中,深度大的那端颜色为 x ,深度小的颜色为 y 的颜色段个数,还要记录一下这三种颜色具体是什么颜色。合并时直接枚举下面半段的上端颜色和上面半段的下端颜色即可,这个复杂度是 O ( 3 4 ) 的。
然后直接倍增 l c a 去跑,如果得到两条链,要把其中一条反过来,再合并成一条。
注意:不能直接记录父亲那样遍历树,因为可能通过不同的边走到同一个儿子,这样相当于遍历树非常多次。颜色相同要直接 b r e a k ,像我这种打了 c o n t i n u e 的就调了好久。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=1e5+7;

using namespace std;

int n,m,test,cnt,x,y,w;
int f[maxn][20];
int ls[maxn],dep[maxn],vis[maxn];

struct edge{
    int x,y,col,next;
}e[maxn*6];

struct rec{
    int a[3][3];
    int cola[3],colb[3];
}g[maxn][20];

void add(int x,int y,int w)
{
    e[++cnt]=(edge){x,y,w,ls[x]};
    ls[x]=cnt;
}

void dfs(int x,int fa)
{
    vis[x]=1;
    f[x][0]=fa;
    dep[x]=dep[fa]+1; 
    for (int i=ls[x];i>0;i=e[i].next)
    {
        int y=e[i].y;
        if (vis[y]) continue;
        dfs(y,x);
    }
}

rec merge(rec x,rec y)
{
    rec z;
    for (int i=0;i<3;i++)
    {
        z.cola[i]=x.cola[i];
        z.colb[i]=y.colb[i];
    }
    for (int i=0;i<3;i++)
    {
        for (int j=0;j<3;j++)
        {
            z.a[i][j]=0;
            for (int k=0;k<3;k++)
            {
                if (!x.colb[k]) continue;
                for (int l=0;l<3;l++)
                {
                    if (!y.cola[l]) continue;
                    z.a[i][j]=max(z.a[i][j],x.a[i][k]+y.a[l][j]-(x.colb[k]==y.cola[l]));
                }
            }
        }
    }
    return z;
}

void calc()
{
    for (int j=1;j<20;j++)
    {
        for (int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=merge(g[i][j-1],g[f[i][j-1]][j-1]);
        }
    }
}

int getans(rec x)
{
    int ans=0;
    for (int i=0;i<3;i++)
    {
        for (int j=0;j<3;j++)
        {
            ans=max(ans,x.a[i][j]);
        }
    }
    return ans;
}

int lca(int x,int y)
{
    if (x==y) return 0;
    if (dep[x]>dep[y]) swap(x,y);
    int d=dep[y]-dep[x],k=19,t=1<<k;
    rec dx,dy;
    int flagx=0,flagy=0;
    while (d)
    {
        if (d>=t)
        {
            if (!flagy) dy=g[y][k],flagy=1;
                   else dy=merge(dy,g[y][k]);
            y=f[y][k];
            d-=t;
        }
        t/=2,k--;
    }
    if (x==y)
    {
        return getans(dy);
    }
    k=19;
    while (k>=0)
    {
        if (f[x][k]!=f[y][k])
        {
            if (!flagx) dx=g[x][k],flagx=1;
                   else dx=merge(dx,g[x][k]);
            if (!flagy) dy=g[y][k],flagy=1;
                   else dy=merge(dy,g[y][k]);
            x=f[x][k],y=f[y][k];
        }
        k--;
    }
    if (!flagx) dx=g[x][0],flagx=1;
           else dx=merge(dx,g[x][0]);
    if (!flagy) dy=g[y][0],flagy=1;
           else dy=merge(dy,g[y][0]);
    for (int i=0;i<3;i++)
    {
        for (int j=0;j<3;j++)
        {
            if (i<j) swap(dy.a[i][j],dy.a[j][i]);
        }
        swap(dy.cola[i],dy.colb[i]);
    }
    return getans(merge(dx,dy));
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&w);
        add(x,y,w);
        add(y,x,w);
    }   
    dfs(1,0);       
    for (int i=1;i<=cnt;i++)
    {
        x=e[i].x,y=e[i].y;
        if (x==f[y][0]) continue;
        for (int j=0;j<3;j++)
        {
            if (g[x][0].cola[j]==e[i].col) break;
            if (!g[x][0].cola[j])
            {
                g[x][0].a[j][j]=1;
                g[x][0].cola[j]=e[i].col;
                g[x][0].colb[j]=e[i].col;
                break;
            }
        }
    }   
    calc();         
    scanf("%d",&test);  
    for (int i=1;i<=test;i++)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81984660
今日推荐