HDU 5877 Weak Pair DFS + 树状数组 + 其实不用离散化

http://acm.hdu.edu.cn/listproblem.php?vol=49

给定一颗树,然后对于每一个节点,找到它的任何一个祖先u,如果num[u] * num[v] <= k。则贡献加1

思路:主要的麻烦就是动态修改前缀和了。因为对于每个数字val。则找它祖先的话, <= k / val的数字,都是合法的。所以问题转化成求你现在dfs中保存的数字,有多少个是  <= k / val的,树状数组即可。

问题就是,数字太大了,这不适合树状数组,那么我们把每个数字离散成他们的下标即可,但其实这部的操作是不需要的,就是不需要用map来离散的,因为一个数组a[],排序后的数组b[],b[i]就是a[]中某一个数字,那么i就是其对应离散后的下标,怎么找这个下标呢?lower_bound即可。但是,有相同的数字的话,lower_bound总是会找第一的,就是2 2 2 2,你找2,返回的是1的。但是没事,这不影响我们的结果。

所以,lower_bound即可,然后找到有多少个数是 <= k / val的,首先找到k / val离散后的下标,那么问题来了,没有k / val这个数值怎么办?那么就找到第一个 <= k / val的就好了。int pos = upper_bound - 1; 。这个不能用lower_bound来找下标了,为什么呢,因为它可能不存在,所以找到一个比它大的了,如果存在,那刚好,不存在又蛋疼,所以用upper_bound找,-1即可。

这题比赛的时候想到怎么做,但是写不出,然后赛后也写了很久,主要是dfs那么什么时候更新,什么时候取消更新,有点乱,还是太渣了啊。。。努力吧

我dfs的思路是这样的:首先没到一个节点cur。算贡献,然后这个节点入树状数组,然后当这个节点的所有边遍历完后,把这个数删除,注意一点是数字可能有0,不能除。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e5 + 20;
LL a[maxn];
LL b[maxn];
int n;
LL k;
struct edge {
    int u, v;
    int tonext;
} e[maxn];
int num, first[maxn];
bool in[maxn];
void addedge(int u, int v) {
    ++num;
    e[num].u = u;
    e[num].v = v;
    e[num].tonext = first[u];
    first[u] = num;
}
LL ans;
int c[maxn];//
int lowbit (int x) { //
    return x&(-x);
}
void add (int pos,int val) { //
    if (pos <= 0) return;
    while (pos<=n) { //
        c[pos] += val;
        pos += lowbit(pos);
    }
    return ;
}
int get_sum (int pos) { //???1--pos???
    if (pos <= 0) return 0;
    int ans = 0;
    while (pos) {
        ans += c[pos];
        pos -= lowbit(pos);
    }
    return ans;
}
void calc(int cur) {
    if (a[cur] == 0) {
        ans += get_sum(n);
    } else {
        LL tofind = k / a[cur];
        int pos = upper_bound(b + 1, b + 1 + n, tofind) - b;
        ans += get_sum(pos - 1);
        //cout << tofind << "   *****" << pos - 1 << endl;
    }
//    cout << cur << " " << ans << endl;
}
void dfs(int cur) {
    calc(cur);
    int pos = lower_bound(b + 1, b + 1 + n, a[cur]) - b;
    add(pos, 1);
    for (int i = first[cur]; i; i = e[i].tonext) {
        dfs(e[i].v);
    }
    add(pos, -1);
}
void work() {
    scanf("%d%I64d", &n, &k);
    for (int i = 1; i <= n; ++i) {
        scanf("%I64d", &a[i]);
        b[i] = a[i];
    }
//    for (int i = 1; i <= n; ++i) {
//        cout << a[i] << " ";
//    }
//    cout << endl;
    sort(b + 1, b + 1 + n);
//    for (int i = 1; i <= n; ++i) {
//        cout << b[i] << " ";
//    }
//    cout << endl;
    for (int i = 1; i <= n - 1; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
        in[v] = 1;
    }
    int root = -inf;
    for (int i = 1; i <= n; ++i) {
        if (in[i] == 0) {
            root = i;
            break;
        }
    }
    ans = 0;
    dfs(root);
//    cout << ans << endl;
    printf("%I64d\n", ans);
    return ;
}
int main () {
#ifdef LOCAL
    freopen("data.txt","r",stdin);
#endif
    int t;
    scanf("%d", &t);
    while (t--) {
        work();
        num = 0;
        memset(in, 0, sizeof in);
        memset(c, 0, sizeof c);
        memset(first, 0, sizeof first);
    }
    return 0;
}

时间: 2024-12-17 21:53:25

HDU 5877 Weak Pair DFS + 树状数组 + 其实不用离散化的相关文章

HDU 5877 Weak Pair(树状数组+dfs+离散化)

http://acm.hdu.edu.cn/showproblem.php?pid=5877 题意: 给出一棵树,每个顶点都有权值,现在要你找出满足要求的点对(u,v)数,u是v的祖先并且a[u]*a[v]<=k. 思路: 转化一下,a[v]<=k/a[u],k/a[u]的最大值也就是k/a[v],也就是寻找<=k/a[v]的个数,到这儿,是不是很像树状数组? 我们只需要从根开始dfs,插入到树状数组中,并且查询即可.注意这道题目需要离散化一下. 1 #include <iostr

2016 大连网赛---Weak Pair(dfs+树状数组)

题目链接 http://acm.split.hdu.edu.cn/showproblem.php?pid=5877 Problem Description You are given a rooted tree of N nodes, labeled from 1 to N. To the ith node a non-negative value ai is assigned.An ordered pair of nodes (u,v) is said to be weak if  (1) u

hdu 5877 Weak Pair dfs序+树状数组+离散化

Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Problem Description You are given a rooted tree of N nodes, labeled from 1 to N. To the ith node a non-negative value ai is assigned.An ordered pair of no

树形DP+树状数组 HDU 5877 Weak Pair

1 //树形DP+树状数组 HDU 5877 Weak Pair 2 // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 3 // 这道题要离散化 4 5 #include <bits/stdc++.h> 6 using namespace std; 7 #define LL long long 8 typedef pair<int,int> pii; 9 const double inf = 12345678901234

HDU 5877 Weak Pair(弱点对)

HDU 5877 Weak Pair(弱点对) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Description 题目描述 You are given a rooted tree of N nodes, labeled from 1 to N. To the ith node a non-negative value ai is assigned. An ordere

hdu 5592 ZYB&#39;s Game 树状数组

ZYB's Game Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5592 Description ZYB has a premutation P,but he only remeber the reverse log of each prefix of the premutation,now he ask you to restore the premutation

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 2852 KiKi&#39;s K-Number (树状数组 &amp;&amp; 二分)

题意:给出对容器的总操作次数n, 接下来是这n个操作.这里对于一个容器提供三种操作, 分别是插入.删除和查找.输入0  e表示插入e.输入1  e表示删除e,若元素不存在输出No Elment!.输入2  e  k表示查找比e大且第k大的数, 若不存在则输出Not Find! 分析:这里考虑树状数组做的原因是在第三个操作的时候, 只要我们记录了元素的总数, 那通过求和操作, 便能够高效地知道到底有多少个数比现在求和的这个数要大, 例如 tot - sum(3)就能知道整个集合里面比3大的数到底有

HDU 3584 Cube (三维 树状数组)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3584 Cube Problem Description Given an N*N*N cube A, whose elements are either 0 or 1. A[i, j, k] means the number in the i-th row , j-th column and k-th layer. Initially we have A[i, j, k] = 0 (1 <= i,