51Nod1588 幸运树

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;
}

猜你喜欢

转载自blog.csdn.net/Wen_Yongqi/article/details/88045348
今日推荐