题目描述
比得喜欢幸运数字。这里所说的幸运数字是由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)是三个不同的序列。
现在要求计算在树中存在多少个这样的三元组关系。
样例解释:
样例一中的16种情况分别为:
(1,2,4),(1,4,2),(2,1,3),(2,1,4),(2,3,1),(2,3,4),(2,4,1),(2,4,3),(3,2,4),(3,4,2),(4,1,2),(4,1,3),(4,2,1),(4,2,3),(4,3,1),(4,3,2)
Input
单组测试数据 第一行包含一个整数n(1≤n≤10^5)。 接下来的n-1行中每行有三个整数 ui vi wi (1≤ui,vi≤n,1≤wi≤10^9) 分别表示有边相连的两个点和这条边的权值。
Output
共一行,表示题目中所要计算的三元组的个数。
Input示例
4 1 2 4 3 1 2 1 4 7
Output示例
16
题解
非常好的一道树上操作的题,一般树上统计个数都是一部分乘一部分(也就是算贡献)。
f[i]表示i这个点的子树中有几个点到i的路径中包含4或7,g[i]表示i这个点的子树外有几个点到i的路径包含4或7。
最后的答案就是Σ(1到n)f[i]*(f[i]-1)+g[i]*(g[i]-1)+f[i]*g[i]*2。具体怎么做可以先跑一边bfs序,然后通过bfs序进行计算。具体的计算在代码里看。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
typedef long long ll;
using namespace std;
const int N=100010;
int pre[N*2],last[N],other[N*2],num,q[N],fa[N],size[N],w[N*2];
int f[N],g[N];
char s[20];
int flag,n;
ll ans;
inline void add(int x,int y,int z){
num++;
pre[num]=last[x];
last[x]=num;
other[num]=y;
w[num]=z;
}
void bfs(){
int head=1,tail=1;
q[1]=1;
while(head<=tail){
int t=q[head];
head++;
size[t]=1;
for(int i=last[t];i;i=pre[i]){
int v=other[i];
if(v!=fa[t]){
fa[v]=t;
q[++tail]=v;
}
}
}
}
int main(){
int x,y;
scanf("%d",&n);
for(int i=1;i<n;i++) {
scanf("%d%d",&x,&y);
scanf("%s",s+1);
int len=strlen(s+1);flag=1;
for(int j=1;j<=len;j++) if(s[j]-'0'!=4&&s[j]-'0'!=7) flag=0;
add(x,y,flag);add(y,x,flag);
}
bfs();
for(int i=n;i>=1;i--){
int t=q[i];
for(int j=last[t];j;j=pre[j]){
int v=other[j];
if(v!=fa[t]) size[t]+=size[v];
}
for(int j=last[t];j;j=pre[j]){
int v=other[j];
if(v==fa[t]) continue;
f[t]+=w[j]?size[v]:f[v];
}
}
for(int i=1;i<=n;i++){
int t=q[i];
for(int j=last[t];j;j=pre[j]){
int v=other[j];
if(v!=fa[t])
g[v]+=w[j]?size[1]-size[v]:g[t]+f[t]-f[v];
}
}
for(int i=1;i<=n;i++)
ans+=(1ll*f[i]*(f[i]-1)+1ll*g[i]*(g[i]-1)+1ll*f[i]*g[i]*2);
printf("%lld\n",ans);
return 0;
}