链接
https://nanti.jisuanke.com/t/39262
题解
f
i
j
f_{ij}
fij表示
i
i
i号点颜色为
j
j
j,整棵子树合法的方案数 暴力就是枚举当前点和儿子节点的颜色 他有个条件说边权两两不同,可以发现在
m
∗
m
m*m
m∗m的
g
c
d
gcd
gcd矩阵中,每个位置上都对应一个转移 而且每个转移肯定都能对应到矩阵中的某个位置 综上所述,有效的转移只有
O
(
m
2
)
O(m^2)
O(m2)个 总的复杂度也就降到
O
(
n
m
+
m
2
)
O(nm+m^2)
O(nm+m2)
代码
#include<bits/stdc++.h>
#define maxn 1010
#define linf (1ll<<60)
#define iinf 0x3f3f3f3f
#define eps 1e-8
#define cl(x) memset(x,0,sizeof(x))
#define mod 1000000007ll
using namespace std
;
typedef long long ll
;
ll
read(ll x
=0)
{
int c
, f
=1;
for(c
=getchar();!isdigit(c
);c
=getchar())if(c
=='-')f
=-f
;
for(;isdigit(c
);c
=getchar())x
=x
*10+c
-48;
return f
*x
;
}
ll N
, M
, head
[maxn
], to
[maxn
], nex
[maxn
], d
[maxn
], etot
, w
[maxn
], f
[maxn
][60], g
[maxn
];
vector
<ll
> tran
[maxn
][maxn
];
set
<ll
> val
;
void adde(ll a
, ll b
, ll c
){to
[++etot
]=b
;w
[etot
]=c
;nex
[etot
]=head
[a
];head
[a
]=etot
;}
ll
gcd(ll a
, ll b
){return !b
?a
:gcd(b
,a
%b
);}
void dfs(ll pos
, ll pre
)
{
ll s
;
for(auto p
=head
[pos
];p
;p
=nex
[p
])
if(to
[p
]!=pre
)
{
dfs(to
[p
],pos
);
}
for(auto c
=1;c
<=M
;c
++)
{
g
[pos
] += f
[pos
][c
];
g
[pos
] %= mod
;
f
[pos
][c
]=1;
for(auto p
=head
[pos
];p
;p
=nex
[p
])
{
if(to
[p
]==pre
)continue;
s
=g
[to
[p
]];
for(auto son
:tran
[c
][w
[p
]])
{
s
-=f
[to
[p
]][son
];
}
f
[pos
][c
] *= s
%mod
;
f
[pos
][c
] %= mod
;
}
g
[pos
] += f
[pos
][c
];
g
[pos
] %= mod
;
}
}
int main()
{
ll a
, b
, c
, i
, j
, ans(0);
N
=read(), M
=read();
for(i
=1;i
<N
;i
++)
{
a
=read(), b
=read(), c
=read();
adde(a
,b
,c
), adde(b
,a
,c
);
val
.emplace(c
);
}
for(i
=1;i
<=M
;i
++)for(j
=1;j
<=M
;j
++)
{
if( val
.find(__gcd(i
,j
)) != val
.end() )
{
tran
[i
][__gcd(i
,j
)].emplace_back(j
);
}
}
dfs(1,0);
for(i
=1;i
<=M
;i
++)
{
ans
=(ans
+f
[1][i
])%mod
;
}
cout
<<(ans
+mod
)%mod
<<endl
;
return 0;
}