2019 计蒜之道 初赛 第一场 C. 商汤AI园区的n个路口(中等)—— 树形DP+优化

This way

题意:

给你n个点,n-1条边,每条边上有一个值,每个值在1到m之间且每条边的值不同。现在让你往点所有上放值,每个点的值在1到m之间且可以相同,要求是任意一条边上的两个点的值的gcd不等于这条边的值,问你有多少种情况。

题解:

上一题是树形DP暴力for每种情况,现在的话暴力是 n 3 n^3 n3也就是1e9的时间复杂度,那么肯定需要优化掉 1 10 \frac{1}{10} 101,怎么优化,我们可以发现对于父亲的值,如果它和这条边的值的gcd不等于这条边的值,那么儿子节点是不是所有情况都可以,那么只需要用一个前缀和维护dp的值就好了,而且这种请况是非常多的,完全可以将到可以接受的范围内。这样的话用时大概是600毫秒,然后对于父亲节点的值和边上的值的gcd与边上的值相同的时候,我们在枚举儿子节点的值的时候可以只枚举边上的倍数,因为如果不是倍数的话是不是gcd就一定不是边上的值了,这样的话时间复杂度可以再优化 1 4 \frac{1}{4} 41左右,状态转移方程非常简单,我们考虑所有儿子,根据加法原理和乘法原理,应该是每个儿子内部的值相加再相互乘:
在这里插入图片描述
也就是第一个儿子所有情况数量乘上第二个儿子所有情况数量…
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
ll dp[1005][1005];
struct node
{
    int to,next,w;
}e[2005];
int n,m,cnt,head[1005];
void add(int x,int y,int w)
{
    e[cnt].to=y;
    e[cnt].next=head[x];
    e[cnt].w=w;
    head[x]=cnt++;
}
void dfs(int x,int fa)
{
    int f=0,one=0;
    for(int i=head[x];~i;i=e[i].next)
    {
        int ne=e[i].to;
        if(e[i].w==1)
            one=1;
        if(ne==fa)
            continue;
        dfs(ne,x);
        f=1;
    }
    for(int i=one?2:1;i<=m;i++)
    {
        dp[x][i]=1;
        for(int j=head[x];~j;j=e[j].next)
        {
            int ne=e[j].to;
            int w=e[j].w;
            if(ne==fa)
                continue;
            if(__gcd(w,i)!=w)
                dp[x][i]=dp[x][i]*dp[ne][m]%mod;
            else
            {
                ll sum=0;
                int pre=0;
                for(int k=w;k<=m;k+=w)
                {
                    if(__gcd(k,i)==w)
                        sum=((sum+dp[ne][k-1]-dp[ne][pre])%mod+mod)%mod,pre=k;
                }
                if(pre!=m)
                    sum=(sum+dp[ne][m]-dp[ne][pre]+mod)%mod;
                dp[x][i]=dp[x][i]*sum%mod;
            }
        }
    }
    for(int i=1;i<=m;i++)
        dp[x][i]=(dp[x][i]+dp[x][i-1])%mod;
}
int main()
{
    memset(head,-1,sizeof(head));
    int x,y,w;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
        scanf("%d%d%d",&x,&y,&w),add(x,y,w),add(y,x,w);
    dfs(1,0);
    return 0*printf("%lld\n",dp[1][m]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值