题目描述
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 行,每行一个自然数,代表该询问的答案。
(CSDN可以拖拽图片上传真是舒服)
task1
题目说是”没有长度超过 2 的简单环”,实际就是一颗有重边的树
(真特么坑)
以为数据很小,所以直接暴力乱搞就行了
task2
树剖?
task3
圆方树?
反正都没写
task4
水法?
task5
因为p=0,所以
int main(){}
就有1分了2333
task6~8
不会
task9
部分分真多
因为一条边可能有很多种颜色,不好处理,但实际上只需要保留任意三种不同的颜色就行了
(当然如果不够三种颜色就全部保留)
证明其合法性,只要证明这样做可以构出最优解
因为题目要找的是一条简单路径,所以如果一条边两端的边颜色与自己的其中两种颜色相同,那么第三种颜色肯定与前后两种不同
因为题目求的是x–>y的路径,所以考虑计算x–>lca和y–>lca,最后将两条路径合并
然后就可以用倍增维护
设f[i][j][0~2][0~2]表示
从点i到i的2^j级父亲的路径,首尾的颜色编号为0~2/0~2的最大答案
为了方便求具体颜色,设fa[i][j]和fa2[i][j]表示i的第2^j级和第(2^j)-1级父亲
求完f后,问题是怎么具体求x–>lca和y–>lca
可以分别维护一个数组,表示从x到当前位置,且结尾颜色为0~2的最大答案
这样每次把相邻的两个f合并更新答案,最后把x和y合并
code
不难想,就是细节很多
调了一个晚上最后发现l写成了k
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
using namespace std;
int a[600001][3];
int b[300001][3];
int ls[300001];
bool bz[300001];
int fa[300001][17];
int fa2[300001][17];
int f[300001][17][3][3];
int dp[300001];
int c[2][3];
int C[3];
int d[2][3];
int x[2];
int Q,n,m,i,j,k,l,len,X,Y,Z,ans,L;
void swap(int &a,int &b) {int c=a;a=b;b=c;}
void New(int x,int y,int z)
{
len++;
a[len][0]=y;
a[len][1]=z;
a[len][2]=ls[x];
ls[x]=len;
}
void dfs(int Fa,int t)
{
int I,i,j,k,l,T;
fa[t][0]=Fa;
fa2[t][0]=t;
fo(i,1,16)
{
fa[t][i]=fa[fa[t][i-1]][i-1];
fa2[t][i]=fa[fa2[t][i-1]][i-1];
}
if (b[t][0]) f[t][0][0][0]=1;
if (b[t][1]) f[t][0][1][1]=1;
if (b[t][2]) f[t][0][2][2]=1;
fo(I,1,16)
if (fa[t][I])
{
T=fa[t][I-1];
fo(i,0,2)
{
fo(j,0,2)
if (f[t][I-1][i][j])
{
fo(k,0,2)
{
fo(l,0,2)
if (f[T][I-1][k][l])
f[t][I][i][l]=max(f[t][I][i][l],f[t][I-1][i][j]-(b[fa2[t][I-1]][j]==b[T][k])+f[T][I-1][k][l]);
}
}
}
}
else
break;
for (i=ls[t]; i; i=a[i][2])
if (a[i][0]!=Fa)
{
if (!b[a[i][0]][0])
b[a[i][0]][0]=a[i][1];
else
if (!b[a[i][0]][1] && b[a[i][0]][0]!=a[i][1])
b[a[i][0]][1]=a[i][1];
else
if (!b[a[i][0]][2] && b[a[i][0]][0]!=a[i][1] && b[a[i][0]][1]!=a[i][1])
b[a[i][0]][2]=a[i][1];
}
bz[t]=1;
for (i=ls[t]; i; i=a[i][2])
if (a[i][0]!=Fa)
{
if (!bz[a[i][0]])
{
dp[a[i][0]]=dp[t]+1;
dfs(t,a[i][0]);
}
}
}
void jump(int t,int i)
{
int j,k,l;
memset(C,0,sizeof(C));
fo(j,0,2)
if (d[t][j])
{
fo(k,0,2)
if (b[x[t]][k])
{
fo(l,0,2)
if (f[x[t]][i][k][l])
C[l]=max(C[l],c[t][j]-(d[t][j]==b[x[t]][k])+f[x[t]][i][k][l]);
}
}
fo(j,0,2)
{
c[t][j]=C[j];
d[t][j]=b[fa2[x[t]][i]][j];
}
x[t]=fa[x[t]][i];
}
int lca(int x,int y)
{
int i;
fd(i,16,0)
if (dp[fa[x][i]]>=dp[y])
x=fa[x][i];
fd(i,16,0)
if (fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
if (x!=y) x=fa[x][0];
return x;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m)
{
scanf("%d%d%d",&X,&Y,&Z);
New(X,Y,Z);
New(Y,X,Z);
}
dp[1]=1;
dfs(0,1);
for (scanf("%d",&Q); Q; Q--)
{
scanf("%d%d",&x[0],&x[1]);
if (x[0]==x[1])
{
printf("0\n");
continue;
}
if (dp[x[0]]<dp[x[1]])
swap(x[0],x[1]);
L=lca(x[0],x[1]);
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
fo(i,0,2)
{
d[0][i]=b[x[0]][i];
if (x[1]!=L)
d[1][i]=b[x[1]][i];
}
x[0]=fa[x[0]][0];
fo(i,0,2)
if (d[0][i])
c[0][i]=1;
if (x[1]!=L)
{
fo(i,0,2)
if (d[1][i])
c[1][i]=1;
x[1]=fa[x[1]][0];
}
else
{
fd(i,16,0)
if (dp[fa[x[0]][i]]>=dp[x[1]])
jump(0,i);
printf("%d\n",max(max(c[0][0],c[0][1]),c[0][2]));
continue;
}
fd(i,16,0)
if (dp[fa[x[0]][i]]>=dp[x[1]])
jump(0,i);
fd(i,16,0)
if (fa[x[0]][i]!=fa[x[1]][i])
jump(0,i),jump(1,i);
if (x[0]!=x[1])
jump(0,0),jump(1,0);
ans=0;
fo(i,0,2)
if (d[0][i])
{
fo(j,0,2)
if (d[1][j])
ans=max(ans,c[0][i]-(d[0][i]==d[1][j])+c[1][j]);
}
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}