Weak Pair 离散化 + 树状数组

本文介绍了一种使用树形DP和二分查找解决特定有根树问题的方法,即计算满足特定条件的点对数量。通过DFS遍历、树状数组求和及权值排序等步骤实现算法。

传送门

题目描述

在一棵有根树上求出有多少个点对(u,v),满足 u是v的祖先且a[u]*a[v] ≤k.

分析

光光跟我说这道题可以用主席树直接秒掉,但是由于我不会主席树,所以只能用一种十分麻烦的做法去处理(下次一定学)

我们可以将所有的权值存入vector里面,排序去重,然后用map进行定位,然后去dfs这棵树,每到达一个节点,去二分查找符合当前条件的最大权值是在vector中的位置,然后用树状数组去求和,然后将这个点的权值的位置的地方+1,dfs下一个节点,回溯的时候 -1即可

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
#include <cstring>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC option("arch=native","tune=native","no-zero-upper")
#pragma GCC target("avx2")
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10,M = 2 * N;
int n;
ll k;
ll a[N];
vector<ll> num;
bool st[N];
int h[N],ne[M],e[M],idx;
int in[N],out[N];
int cnt;
int tr[N];
ll ans;
map<ll,int> pp;

int lowbit(int x){
    return x & -x;
}

void add(int x,int y){
    ne[idx] = h[x],e[idx] = y,h[x] = idx++;
}

int find(ll x){
    return upper_bound(num.begin(),num.end(),x) - num.begin();
}

void ad(int x,int c){
    for(int i = x;i <= n;i += lowbit(i)) tr[i] += c;
}

int sum(int x){
    int res = 0;
    for(int i = x;i;i -= lowbit(i)) res += tr[i];
    return res;
}

void dfs(int u){
    ll x = k / a[u];
    int p = find(x);
    p--;
    ans += sum(p);
    ad(pp[a[u]],1);
    for(int i = h[u];~i;i = ne[i]){
        int j = e[i];
        dfs(j);
    }
    ad(pp[a[u]],-1);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        pp.clear();
        num.clear();
        idx = 0;
        scanf("%d%lld",&n,&k);
        memset(h,-1,sizeof h);
        memset(tr,0,sizeof h);
        memset(st,0,sizeof st);
        num.push_back(-1);
        for(int i = 1;i <= n;i++) {
            scanf("%lld",&a[i]);
            num.push_back(a[i]);
        }
        for(int i = 1;i < n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            st[y] = 1;
        }
        int root = -1;
        for(int i = 1;i <= n;i++){
            if(!st[i]){
                root = i;
                break;
            }
        }
        sort(num.begin(),num.end());
        num.erase(unique(num.begin(),num.end()),num.end());
        for(int i = 1;i < num.size();i++){
            pp[num[i]] = i;
        }
        ans = 0;
        dfs(root);
        printf("%lld\n",ans);
    }
}

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*6
*6
*4
*/
### C++中 `set` 和 `pair` 组合时的排序规则 在C++中,`std::set` 是一种关联容器,其元素默认按照升序排列。当 `std::set` 的元素类型为 `std::pair` 时,需要明确如何对这些 `pair` 进行排序。如果默认排序规则不满足需求,可以通过自定义比较函数来实现特定的排序逻辑。 #### 默认排序规则 `std::set<std::pair<int, int>>` 默认使用 `std::less<std::pair<int, int>>` 作为比较器,这意味着它会根据 `pair` 的第一个元素(`first`)进行升序排序;如果 `first` 相等,则根据第二个元素(`second`)进行升序排序[^2]。 ```cpp #include <set> #include <iostream> int main() { std::set<std::pair<int, int>> s = { {3, 1}, {1, 2}, {3, 2}, {2, 3} }; for (const auto& p : s) { std::cout << "{" << p.first << ", " << p.second << "} "; } // 输出:{1, 2} {2, 3} {3, 1} {3, 2} return 0; } ``` #### 自定义排序规则 当需要对 `pair` 使用不同于默认规则的排序方式时,可以定义一个自定义比较器。以下是一个示例,展示了如何通过结构体或 lambda 表达式重新定义排序逻辑[^1]。 ##### 示例 1:使用结构体定义比较器 ```cpp struct pair_comp { bool operator()(const std::pair<int, int>& A, const std::pair<int, int>& B) const { if (A.first * B.second < A.second * B.first) { return true; } else if (A.first * B.second == A.second * B.first) { return A.first < B.first; } else { return false; } } }; int main() { std::set<std::pair<int, int>, pair_comp> sets = { {1, 2}, {2, 3}, {3, 4}, {4, 5} }; for (const auto& p : sets) { std::cout << "{" << p.first << ", " << p.second << "} "; } // 输出取决于自定义比较器的逻辑 return 0; } ``` ##### 示例 2:使用 lambda 表达式定义比较器 从 C++14 开始,可以使用 lambda 表达式直接定义比较器,简化代码[^3]。 ```cpp auto comp = [](const std::pair<int, int>& A, const std::pair<int, int>& B) { if (A.first * B.second < A.second * B.first) { return true; } else if (A.first * B.second == A.second * B.first) { return A.first < B.first; } else { return false; } }; int main() { std::set<std::pair<int, int>, decltype(comp)> sets(comp); sets.insert({1, 2}); sets.insert({2, 3}); sets.insert({3, 4}); for (const auto& p : sets) { std::cout << "{" << p.first << ", " << p.second << "} "; } // 输出取决于自定义比较器的逻辑 return 0; } ``` #### 注意事项 - 自定义比较器必须满足严格弱序(Strict Weak Ordering)。即对于任意两个元素 `a` 和 `b`,比较器的结果应确保一致性[^2]。 - 如果需要将 `pair` 用作 `unordered_set` 或 `unordered_map` 的键值,则需要定义哈希函数,避免潜在的冲突问题[^4]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值