题目链接HDOJ 5877
题意就是给你一棵树 定义一个关系为一个祖先点 与 其所有子孙满足权值相乘小于等于k的关系对有多少个
我们不难想到这类题目适合用dfs序去解决 也就是遍历到一个子孙点的时候 去找他的祖先点 有多少个是比 k/a[u]要小的
那么我们如何利用树状数组呢?不能直接建dfs序的树状数组 我们要在dfs的过程中 不断的压入子孙进树状数组
然后统计满足条件的k/a[u] 小的有多少个 再出去的时候要压出子孙 这样就可以的 但是k很大 所以我们要离散化 离散化注意第一个是找upper_bound因为题目要求的是小于等于 然后k/a[u]不要小数值 整数值就可以了
/*
hdoj 5877
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <set>
#include <sstream>
using namespace std;
const int MAX_N = 200024;
struct edge {
int v,next;
}e[MAX_N<<1];
int eid ,num;
long long k;
long long a[MAX_N],b[MAX_N],c[MAX_N];
int p[MAX_N],low[MAX_N],high[MAX_N],dep,vis[MAX_N],fa[MAX_N];
void init(){
memset(p,-1,sizeof(p));
memset(fa,0,sizeof(fa));
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
dep = 0;
eid = 0;
}
void Insert(int u,int v){
e[eid].v = v;
e[eid].next = p[u];
p[u] = eid++;
}
void add(int x,int v){
for(;x<MAX_N;x+=x&(-x))
c[x]+=v;
}
long long getsum(int x){
long long ans = 0;
for(;x;x-=x&(-x))
ans+=c[x];
return ans;
}
long long ans = 0;
void dfs(int u){
int x= upper_bound(b+1,b+1+num,k/a[u])-b-1;
int pos = lower_bound(b+1,b+1+num,a[u])-b;
ans+=getsum(x);
add(pos,1);
vis[u] = 1;
for(int i = p[u];i!=-1;i=e[i].next)
if(!vis[e[i].v]) dfs(e[i].v);
add(pos,-1);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
ans = 0;
init();
int n;
int m = 0;
scanf("%d%lld",&n,&k);
for(int i = 1;i<=n;++i){
scanf("%lld",&a[i]);
b[++m] = a[i];
if(a[i]==0) b[++m] = 1e19;
else b[++m] = k/a[i];
}
sort(b+1,b+1+m);
num = unique(b+1,b+1+m)-b-1;
for(int i = 1;i<n;++i){
int x,y;
scanf("%d%d",&x,&y);
Insert(x,y);
Insert(y,x);
fa[y]++;
}
for(int i = 1;i<=n;++i){
if(fa[i]==0){
dfs(i);
break;
}
}
printf("%lld\n",ans);
}
return 0;
}