2023牛客暑期多校训练营8 D.Distance on Tree

题目大意

给定一颗大小为 n n n ,根节点为 1 1 1 的树,和 n n n 个点的点权 a i a_i ai n − 1 n-1 n1 条边的边权 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+awki(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 1n,m2000,1q,wi2105,0ki,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+2wison)
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+2wison,dppre_son,1,a+dpson,1,ja+2wison,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,ra+2wison,dppre_son,2,a+dpson,1,ra+2wison)
我们发现,如果直接处理子树合并,它的复杂度会到达 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}} m n
小点和大点合并,复杂度为 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]));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值