题目大意
给定一颗大小为
n
n
n ,根节点为
1
1
1 的树,和
n
n
n 个点的点权
a
i
a_i
ai,
n
−
1
n-1
n−1 条边的边权
w
i
w_i
wi,和一个值
m
m
m。有
q
q
q 个询问,每次给定两个数
x
i
,
k
i
x_i,k_i
xi,ki ,在
x
i
x_i
xi 的子树下求三个节点
u
,
v
,
w
u,v,w
u,v,w ,满足
a
u
+
a
v
+
a
w
≡
k
i
(
m
o
d
m
)
a_u+a_v+a_w \equiv k_i(mod\ m)
au+av+aw≡ki(mod m),且
d
i
s
(
u
,
v
)
+
d
i
s
(
u
,
w
)
+
d
i
s
(
v
,
w
)
dis(u,v)+dis(u,w)+dis(v,w)
dis(u,v)+dis(u,w)+dis(v,w) 的值尽可能大。
定义
d
i
s
(
a
,
b
)
dis(a,b)
dis(a,b) 表示节点
a
a
a 和
b
b
b 的最短路径边权之和。
1
≤
n
,
m
≤
2000
,
1
≤
q
,
w
i
≤
2
∗
1
0
5
,
0
≤
k
i
,
a
i
<
m
1\leq n,m\leq 2000,1\leq q,w_i \leq 2*10^5,0\leq k_i,a_i<m
1≤n,m≤2000,1≤q,wi≤2∗105,0≤ki,ai<m
题解
参考 walk alone 的新手向题解==
首先,它的询问数非常之大,甚至不能
O
(
n
)
O(n)
O(n) 遍历完。
所以我们考虑离线的操作,
计算一个
x
i
x_i
xi 时往往会对不同的
k
i
k_i
ki 做出贡献,所以我们可以将所有的答案全都求出,再进行输出。
显然的,一个节点的状态可以由它的子节点推过来,所以我们选择用树形DP求解。
设
d
p
i
,
1
/
2
/
3
,
j
dp_{i,1/2/3,j}
dpi,1/2/3,j 表示在节点
i
i
i 的子树内,
1
/
2
/
3
1/2/3
1/2/3 个子节点点权之和模
m
m
m 后结果为
j
j
j 的到
i
i
i 距离的两倍(方便写定义式)。
显然的有
d
p
i
,
1
,
j
=
m
a
x
(
d
p
s
o
n
,
1
,
j
+
2
∗
w
i
−
s
o
n
)
dp_{i,1,j}=max(dp_{son,1,j}+2*w_{i-son})
dpi,1,j=max(dpson,1,j+2∗wi−son)
d
p
i
,
2
,
j
=
m
a
x
(
d
p
s
o
n
,
2
,
j
+
2
∗
w
i
−
s
o
n
,
d
p
p
r
e
_
s
o
n
,
1
,
a
+
d
p
s
o
n
,
1
,
j
−
a
+
2
∗
w
i
−
s
o
n
,
d
p
i
,
2
,
j
)
dp_{i,2,j}=max(dp_{son,2,j}+2*w_{i-son},dp_{pre\_son,1,a}+dp_{son,1,j-a}+2*w_{i-son},dp_{i,2,j})
dpi,2,j=max(dpson,2,j+2∗wi−son,dppre_son,1,a+dpson,1,j−a+2∗wi−son,dpi,2,j)
d
p
i
,
3
,
j
=
m
a
x
(
d
p
s
o
n
,
3
,
j
,
d
p
i
,
3
,
j
,
d
p
p
r
e
_
s
o
n
,
1
,
a
+
d
p
s
o
n
,
2
,
r
−
a
+
2
∗
w
i
−
s
o
n
,
d
p
p
r
e
_
s
o
n
,
2
,
a
+
d
p
s
o
n
,
1
,
r
−
a
+
2
∗
w
i
−
s
o
n
)
dp_{i,3,j}=max(dp_{son,3,j},dp_{i,3,j},dp_{pre\_son,1,a}+dp_{son,2,r-a}+2*w_{i-son},dp_{pre\_son,2,a}+dp_{son,1,r-a}+2*w_{i-son})
dpi,3,j=max(dpson,3,j,dpi,3,j,dppre_son,1,a+dpson,2,r−a+2∗wi−son,dppre_son,2,a+dpson,1,r−a+2∗wi−son)
我们发现,如果直接处理子树合并,它的复杂度会到达
O
(
n
m
2
)
O(nm^2)
O(nm2)
讨论:
合并子树时,有许多
d
p
i
,
1
/
2
/
3
,
j
dp_{i,1/2/3,j}
dpi,1/2/3,j 的
j
j
j 范围没有到达
m
m
m 在维护这些点时,可以直接枚举子节点中有值的合并转移。
以
m
\sqrt{m}
m 为分界线,
定义子树节点
<
m
<\sqrt{m}
<m 的为小点,反之为大点,
小点和大点合并,复杂度为
m
m
m
大点和大点合并,复杂度为
m
2
m^2
m2,
在一棵树中,大点的个数不大于
n
m
\frac{n}{\sqrt{m}}
mn,
小点和大点合并,复杂度为
n
m
n \sqrt{m}
nm
所以总复杂度为
n
m
3
2
nm^{\frac{3}{2}}
nm23
故复杂度合理。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+5;
struct node{
int to,w;
};
vector<node> v[N];
int dp[N][4][N];
int l1[N],l2[N];
int f[N],a[N],sum[N];
int n,m,q;
int ans=0,t1,t2;
void merge(int dp[],int a[],int b[],int w) //合并子节点
{
t1=0,t2=0;
for(int i=0;i<m;i++) //只找有值的维护
{
if(a[i]>=0)
l1[++t1]=i;
if(b[i]>=0)
l2[++t2]=i;
}
for(int i=1;i<=t1;i++)
for(int j=1;j<=t2;j++)
dp[(l1[i]+l2[j])%m]=max(dp[(l1[i]+l2[j])%m],a[l1[i]]+b[l2[j]]+2*w);
}
void dfs(int x,int fa)
{
dp[x][1][f[x]]=0;
for(int i=0;i<v[x].size();i++)
{
node son=v[x][i];
if(son.to==fa)
continue;
dfs(son.to,x);
for(int i=3;i>=1;i--)
{
for(int j=1;j<i;j++)
merge(dp[x][i],dp[son.to][j],dp[x][i-j],son.w);
for(int j=0;j<m;j++)
{
if(dp[son.to][i][j]!=-1)
dp[x][i][j]=max(dp[x][i][j],dp[son.to][i][j]+(i<=2)*(2*son.w));
// cout <<dp[x][i][j]<<" ";
}
// puts("");
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
scanf("%d",&f[i]);
for(int i=1;i<n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
v[a].push_back({b,c});
v[b].push_back({a,c});
}
for(int i=1;i<=n;i++)
for(int j=1;j<=3;j++)
for(int k=0;k<m;k++)
dp[i][j][k]=-1;
dfs(1,0);
while(q--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",max(0,dp[a][3][b]));
}
}