http://www.51nod.com/Challenge/Problem.html#!#problemId=1588
比得喜欢幸运数字。这里所说的幸运数字是由4和7组成的正整数。比如,数字47,744,4是幸运数字,而5,17,467就不是。
一天,比得遇到一棵由n个点组成的树。另外,这棵树是带权的,即每条边有一个权值(由一个正整数表示)。如果一条边的权值是一个幸运数字,那么我们就说这条边是一条幸运边。说明一下,一棵n个结点的树是由n个结点和n-1条边组的无环的无向图。
比得好奇,在树中有多少个满足以下条件的三元组tr(i,j,k)(i,j,k是三个不同的点)。
1.i到j有路径,i到k也有路径
2.每条路径中至少有一条幸运边。
数字的顺序是有意义的,举例说明,tr(1,2,3),tr(1,3,2),tr(2,1,3)是三个不同的序列。
现在要求计算在树中存在多少个这样的三元组关系。
思路:
要想不重不漏地枚举所有组合,我们枚举所有的i,对于每一个i,找到i所对应的(j,k)的组合数,加起来就行了。
令 f[u] 为在 u 的子树内,到 u 的路径中有幸运边的点有几个,令 g[u] 为在 u 的子树外,到 u 的路径中有幸运边的点有几个。
那么(u,j,k)的方案数=f[u]∗(f[u]−1)+g[u]∗(g[u]−1)+f[u]∗g[u]∗2
转移:
边(u,v)是幸运边:f[u]+=size[v]边(u,v)是幸运边:f[u]+=size[v]
边(u,v)不是幸运边:f[u]+=f[v]边(u,v)不是幸运边:f[u]+=f[v]
边(fa,u)是幸运边:g[u]=size[1]−size[u](1是根)边(fa,u)是幸运边:g[u]=size[1]−size[u](1是根)
边(fa,u)不是幸运边:g[u]=g[fa]+f[fa]−f[u]
这篇写的非常好:https://blog.csdn.net/white945/article/details/78240990
#include<bits/stdc++.h>
using namespace std;
#define maxn 100000+100
#define ll long long
int n,in[maxn],out[maxn],sz[maxn];
vector<int> G[maxn],w[maxn];
ll ans;
int min(int a,int b,int c){return min(min(a,b),c);}
void dfs(int u,int fa)
{
sz[u]=1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa)continue;
dfs(v,u);
sz[u]+=sz[v];
if(w[u][i])in[u]+=sz[v];
else in[u]+=in[v];
}
}
void dfs2(int u,int fa)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa)continue;
if(w[u][i])out[v]=sz[1]-sz[v];
else out[v]=in[u]-in[v]+out[u];
dfs2(v,u);
}
}
int check(int w)
{
bool ok=1;
while(w)
{
if(w%10!=4&&w%10!=7)
{
ok=0;break;
}
w/=10;
}
return ok;
}
int main()
{
// freopen("input.in","r",stdin);
int x,y,z;
cin>>n;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
G[x].push_back(y);w[x].push_back(check(z));
G[y].push_back(x);w[y].push_back(check(z));
}
dfs(1,0);
dfs2(1,0);
for(int u=1;u<=n;u++)ans+=(ll)in[u]*(in[u]-1)+(ll)out[u]*(out[u]-1)+(ll)in[u]*out[u]*2;
cout<<ans<<endl;
return 0;
}