HDU5877 Weak Pair(树状数组+dfs)

本文介绍了一种在给定树结构中查找弱对的算法。弱对定义为一棵树中的节点与其祖先组成的对,且该对节点权值的乘积不大于预设阈值K。文章详细阐述了算法的具体实现步骤,包括节点权值离散化处理、树状数组的应用等。

Weak Pair

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1161    Accepted Submission(s): 392


Problem Description
You are given a rooted tree of N nodes, labeled from 1 to N . To the i th node a non-negative value ai is assigned.An ordered pair of nodes (u,v) is said to be weak if
  (1) u is an ancestor of v (Note: In this problem a node u is not considered an ancestor of itself);
  (2) au×avk .

Can you find the number of weak pairs in the tree?
 

Input
There are multiple cases in the data set.
  The first line of input contains an integer T denoting number of test cases.
  For each case, the first line contains two space-separated integers, N and k , respectively.
  The second line contains N space-separated integers, denoting a1 to aN .
  Each of the subsequent lines contains two space-separated integers defining an edge connecting nodes u and v , where node u is the parent of node v .

  Constrains:
  
   1N105
  
   0ai109
  
   0k1018
 

Output
For each test case, print a single integer on a single line denoting the number of weak pairs in the tree.
 

Sample Input
      
1 2 3 1 2 1 2
 

Sample Output
      
1
 

Source
题意:有一棵树,N个结点,N-1条边,每个结点有权值ai,如果存在一个点对,由结点和它的祖先组成,
并且它们相乘的结果<=K,那么这个点对就称为weak,求有多少个这样的点对。
思路:先将ai和K/ai离散化,然后找到根节点遍历树,每插入一个结点前,用树状数组求当前小于K/ai的结点
有多少个并加入结果,然后再将树状数组ai的位置+1,每次回溯前将树状数组ai的位置-1。注意ai可能为0,所
以K/ai要处理一下。
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
#define maxn 100010
ll bit[maxn*2], a[maxn], b[maxn*2], ans, K;
int N, n, tot, head[maxn];
bool vis[maxn];
void add(ll bit[], int i, ll x){
    while(i <= n){
        bit[i] += x;
        i += i&-i;
    }
}
ll sum(ll bit[], int i){
    ll s = 0;
    while(i > 0){
        s += bit[i];
        i -= i&-i;
    }
    return s;
}
struct Edge{
    int to, next;
}edge[maxn];
void addedge(int u, int v){
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void dfs(int now){
    vis[now] = 1;
    //printf("%d ", now);
    ll loc1, loc2;
    if(a[now] == 0) loc1 = n;
    else {
        ll point = K/a[now];
        //printf("%I64d ", point);
        loc1 = lower_bound(b, b+n, point)-b+1;
    }
    loc2 = lower_bound(b, b+n, a[now])-b+1;
    //printf("%I64d\n", sum(bit, loc1));
    ans += sum(bit, loc1);
    add(bit, loc2, 1);
    for(int i = head[now];i != -1;i = edge[i].next){
        int son = edge[i].to;
        if(vis[son]) continue;
        dfs(son);
    }
    add(bit, loc2, -1);
}
int main()
{
    int i, T, top;
    scanf("%d", &T);
    while(T--){
        scanf("%d %I64d", &N, &K);
        top = 0;
        for(i = 1;i <= N;i++){
            scanf("%I64d", &a[i]);
            b[top++] = a[i];
            if(a[i] != 0) b[top++] = K/a[i];
        }
        sort(b, b+top);
        n = unique(b, b+top)-b;
        int u, v;
        memset(head, -1, sizeof head);
        memset(bit, 0, sizeof bit);
        memset(vis, 0, sizeof vis);
        tot = 0;
        ans = 0;
        for(i = 0;i < N-1;i++){
            scanf("%d %d", &u, &v);
            addedge(u, v);
            vis[v] = 1;
        }
        int start;
        for(i = 1;i <= N;i++)
            if(!vis[i]){
                start = i;
                break;
            }
        memset(vis, 0, sizeof vis);
        dfs(start);
        printf("%I64d\n", ans);
    }
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值