题意:
n个节点的树,节点的点权为ai,要求找出有多少个二元组(u,v)满足
1:u是v的祖先且u!=v
2:a[u]*a[v]<=K
题解:
对于单个节点而言,可以讨论以这个节点为v,有多少祖先和其相乘满足条件,即算单点对答案的贡献度。
这题很容易就能转化成:
对单个节点而言,统计比当前节点大的祖先k/ai;
由此,可以用树状数组维护从根节点开始的k/ai值。
每遍历到一个节点,求出树状数组里比当前ai大的数的个数。
再将其的k/ai值植入树状数组。
还有一点遍历过的节点要及时删除。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int n,m;
typedef long long ll;
long long k;
ll val[N],b[N],f[N],tr[N];
vector<int> v[N];
#define lowbit(x) (x)&(-x)
int getid(long long x) {
return lower_bound(b+1,b+m+1,x)-b;
}
void add(int x,int val)
{
while(x<N-50)
{
tr[x]+=val;
x+=lowbit(x);
}
}
ll sum(int x)
{
ll res=0;
while(x>0)
{
res+=tr[x];
x-=lowbit(x);
}
return res;
}
ll res;
void dfs(int x)
{
int h=(lower_bound(b+1,b+m+1,k/val[x]+1)-b)-1;//第一个大于等于k/val[x]的数再减1得到最后一个
//得到最后一个小于等于 k/val[x] 的数
res+=sum(h);
add(getid(val[x]),1);
for(int i=0;i<v[x].size();i++)
{
dfs(v[x][i]);
}
add(getid(val[x]),-1);
}
int main()
{
int t;
while(scanf("%d",&t)!=EOF)
{
while(t--)
{
memset(tr,0,sizeof(tr));
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
{
v[i].clear();
f[i]=0;
scanf("%lld",&val[i]);
b[i]=val[i];
}
sort(b+1,b+1+n);
m=unique(b+1,b+1+n)-(b+1);
for(int i=0;i<n-1;i++)
{
int c,d;
scanf("%d%d",&c,&d);
v[c].push_back(d);
f[d]=1;
}
res=0;
for(int i=1;i<=n;i++)
{
if(!f[i])
{
dfs(i);
break;
}
}
printf("%lld\n",res );
}
}
}