题意:
给你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]);
}