[HDU 8577] Weak Pair (DFS+线段树)

链接

HDU 8577


题意

给出一棵树,每个节点含有一个权值,问有多少个节点对满足存在祖先后代关系,并且满足权值之积不大于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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值