链接
题意
给出一棵树,每个节点含有一个权值,问有多少个节点对满足存在祖先后代关系,并且满足权值之积不大于k。
思路
对树进行一次深搜,很容易保存某节点所有祖先的权值,这里我们需要把到达节点的祖先的权值维护成一个有序表,这样我们算出和当前节点的权值相乘不大于k的最大因子,在有序表里查询不大于k的权值个数即可。
这样的有序表可以用线段树维护,但是权值范围太大,需要做一次离散化(sort+unique+lower_bound),每次查找出因子的位置,再query线段树即可。
PS:见别的牛人用treap树也可以做,我不会写平衡二叉树,所以想到了这个办法,感觉也不错,线段树赛高。
代码
#include <cstdio>
#include <algorithm>
#include <vector>
#include <iostream>
#include <cstring>
#include <map>
using namespace std;
typedef long long lint;
#define maxn (101000)
struct _node
{
int w, fa;
vector<int> son;
} tree[maxn];
int n, root;
lint k, o;
vector<lint> h;
int seg[maxn << 2];
inline int lowbit(int x)
{
return (x&-x);
}
void add(int x, int value, int n)
{
for(int i = x; i <= n; i += lowbit(i))
{
seg[i] += value;
}
}
int get(int x)
{
int sum = 0;
for(int i = x; i; i -= lowbit(i))
sum += seg[i];
return sum;
}
void DFS(int u)
{
if(u != root)
{
lint mk = k / tree[u].w;
int p = lower_bound(h.begin(), h.end(), mk) - h.begin();
if(p < h.size() && h[p] == mk) p++;
o += get(p);
}
int p = lower_bound(h.begin(), h.end(), tree[u].w) - h.begin();
add(p + 1, 1, n << 2);
for(int i = 0, v; i < tree[u].son.size(); i++)
{
v = tree[u].son[i];
DFS(v);
}
add(p + 1, -1, n << 2);
}
int main()
{
int T;
cin >> T;
while(T--)
{
cin >> n >> k;
h.clear();
for(int i = 1, w; i <= n; i++)
{
scanf("%d", &w);
tree[i].w = w;
tree[i].fa = 0;
tree[i].son.clear();
h.push_back(w);
}
sort(h.begin(), h.end());
h.erase(unique(h.begin(), h.end()), h.end());
for(int i = 0, u, v; i < n-1; i++)
{
scanf("%d%d", &u, &v);
tree[u].son.push_back(v);
tree[v].fa = u;
}
root = n;
while(tree[root].fa)
root = tree[root].fa;
o = 0;
memset(seg, 0, sizeof(int) * ((n + 1) * 4));
DFS(root);
cout << o << endl;
}
return 0;
}