寒武纪测试赛E题题解-Travel

这个博客不再更新,新博客地址请戳

链接:http://aiiage.hustoj.com/contest.php?cid=1000

题意:
给你n(n<=3000)个点 n-1 条边的一棵树,每条边有一个权值w(w<=1000),现在有Q个询问:在距离mod k的意义下,距离 u 点最远的点有多远?

第一行输入n,表示n个点
接下来n-1行,输入u,v,w,表示一条边,w是权值,点从1开始
输入一个Q,表示询问次数(Q<=1e5)
接下来Q行,输入u,k(2<=k<=100)

对于每次询问,输出在距离mod k的意义下,距离 u 点最远的点有多远

样例如下:

输入:
3
1 2 7
1 3 6
5
1 9
1 7
3 2
3 15
2 7

输出:
7
6
1
13
6

思路1:暴力算出所有点对 (u,v)之间的距离dis[u][v],对于每次询问(u,k),遍历一遍
dis[u][i]%k的值,取大。
时间复杂度:O(nn + Qnk) ,超时
空间复杂度:O(n
n)

思路2:暴力算出所有点对(u,v)之间的距离dis(u,v),再算出对于每个可能的k值取模后的结果,在这个过程中对得到的结果取大。
时间复杂度:O(nnk+Q),超时
空间复杂度:O(n*k)

思路3:
1.我们把无根树看成有根树

2.对于结点u,满足mod k距离最大的结点要么在 u 的子树上,要么在 u 的父亲那头,我们先求在 u 的子树上的满足情况。(后面再解释)

3.设dp1[u][j][k]表示对于结点u,在u的子树上,满足距离%k 后,与 u 距离为 j 的结点数

4.那么dp1[u][j][k]应该等于所有dp1[v][(j-w%k+k)%k][k]之和,其中v表示u的儿子,w表示(u,v)的距离。
为什么是(j-w%k+k)%k?
要得到u与v的子树里距离是j的结点个数,已知u与v距离为w,那么问题就变成了求v与v的子树里距离是j-w的结点个数。

5.知道子树里的还不行,我们不难得出,除了root之外,所有其他结点都还没考虑父亲另一头的结点。

6.这时候我们设dp[u][j][k]表示满足 dis(u,v)%k==j 的v的总个数。

7.我们已知子树里满足条件的结点数了,那么如何求出剩余的呢?设dis(u,u的父亲)=w,则父亲那头与u距离为j的结点数是:dp[father u][(j-w+k)%k][k]个。

8.这时候我们发现对于u的父亲,距离为j-w的结点不只是从另一边得到,还可能从u的子树得到,因此我们要减去这一部分重复的。设距离u的父亲为j-w的结点为v,所以dis(v,father u)==j-w,又知道dis(father u,u)==w,所以dis(u,v)的距离为j-2w,从前面已知
dp1[u][(j-2*w%k+k)%k][k],因此可以得到

	dp[v][j][k]=dp1[v][j][k]+
                dp[u][(j-w%k+k)%k][k]-
                dp1[v][(j-(2*w)%k+2*k)%k][k];

时间复杂度:O(nkk+Qk)
空间复杂度:O(n
k*k),如果用int会爆内存,在不影响答案正确性的情况下,把int改为short,就不会爆内存了

#include <bits/stdc++.h>
using namespace std;
struct node
{
    short v,w;
    node(){}
    node(int v,int w):v(v),w(w){}
};
int ans[3001][101];
vector<node> g[3001];
short dp1[3001][101][101];
short dp[3001][101][101];
short fa[3001];
void getfa(int u,int pre)
{
    int w;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].v;
        if(v==pre){w=g[u][i].w;continue;}
        fa[v]=u;
        getfa(v,u);
    }
    if(pre==-1)
    {
        for(int k=2;k<=100;k++)
            for(int j=0;j<k;j++)
                dp[u][j][k]+=dp1[u][j][k];
    }
    else
    {
        for(int k=2;k<=100;k++)
            for(int j=0;j<k;j++)
                dp1[pre][j][k]+=dp1[u][(j-w%k+k)%k][k];
    }
}
 
void dfs(int u)
{
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].v,w=g[u][i].w;
        if(v==fa[u])continue;
        for(int k=2;k<=100;k++)
            for(int j=0;j<k;j++)
                dp[v][j][k]=dp1[v][j][k]+
                            dp[u][(j-w%k+k)%k][k]-
                            dp1[v][(j-2*(w%k)+2*k)%k][k];
        dfs(v);
    }
}
 
int main()
{
    int n;
    scanf("%d",&n);
    int u,v,w;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        g[u].push_back(node(v,w));
        g[v].push_back(node(u,w));
    }
    for(u=1;u<=n;u++)
        for(int k=2;k<=100;k++)
            dp1[u][0][k]=1;
    getfa(1,-1);
    dfs(1);
    for(u=1;u<=n;u++)
    {
        for(int k=2;k<=100;k++)
        {
            for(int j=k-1;j;j--)
            {
                if(dp[u][j][k])
                {
                    ans[u][k]=j;
                    break;
                }
            }
        }
    }
 
    int q;
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&u,&w);
        printf("%d\n",ans[u][w]);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1005
    User: xtuacm1
    Language: C++
    Result: 正确
    Time:1130 ms
    Memory:122556 kb
****************************************************************/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值