Description
给定一个无向连通图,
n
n
个点(下标从 1 开始), 条边,每条边有一个颜色。保证无自环,没有长度超过
2
2
的简单环。
现有 个询问:给出两个点
x
x
、,选择一条
x
x
到 简单路径(不经过重复的点),经过的边将形成一个颜色序列,价值为相同颜色的极大连续段个数,求出最大的价值。
Input
第一行,一个正整数
n
n
,一个自然数 。
接下来
m
m
行,每行三个正整数 ,代表一条边
(u,v)
(
u
,
v
)
,颜色为
w
w
。接下来一行,一个自然数 。
接下来
q
q
行,每行两个正整数 ,代表一次询问。
Output
共
q
q
行,每行一个自然数,代表该询问的答案。
Data Constraint
分析:
好狗的一道题,一开始连题目都看错了,询问的是颜色段的个数而不是最长段的长度。
然后我打了一个300多行的链剖
首先因为没有自环和长度大于的环,所以相当于树有许多重边。
显然我们要颜色尽量不同,我们考虑一条边颜色尽量和前面的不一样。考虑一条边的贡献,他只要记录下三种不同的颜色即可(因为一条边最多与两条边相邻,要选不同只需记录
3
3
条边)。
考虑倍增求,我们设表示
i
i
向上走步的路径中,深度大的那端颜色为
x
x
,深度小的颜色为的颜色段个数,还要记录一下这三种颜色具体是什么颜色。合并时直接枚举下面半段的上端颜色和上面半段的下端颜色即可,这个复杂度是
O(34)
O
(
3
4
)
的。
然后直接倍增
lca
l
c
a
去跑,如果得到两条链,要把其中一条反过来,再合并成一条。
注意:不能直接记录父亲那样遍历树,因为可能通过不同的边走到同一个儿子,这样相当于遍历树非常多次。颜色相同要直接
break
b
r
e
a
k
,像我这种打了
continue
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));
}
}