2019 计蒜之道初赛第一场 C.商汤AI园区的n个路口(中等)

链接

https://nanti.jisuanke.com/t/39262

题解

f i j f_{ij} fij表示 i i i号点颜色为 j j j,整棵子树合法的方案数
暴力就是枚举当前点和儿子节点的颜色
他有个条件说边权两两不同,可以发现在 m ∗ m m*m mm 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值