hdu 5877 dfs+离散化+树状数组

本文介绍了一种解决弱对问题的方法,通过树状数组和离散化的应用,在一棵带权值的树中寻找符合条件的弱对。利用DFS遍历树,并结合树状数组优化查找过程,实现了高效求解。

传送门;HDU 5877 Weak Pair

描述:

Weak Pair

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


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
 

Recommend
wange2014   |   We have carefully selected several similar problems for you:   5873  5872  5871  5870  5869 
 

题意:

有一棵树,N个结点,N-1条边,每个结点有权值ai,如果存在一个点对,由结点和它的祖先组成,
并且它们相乘的结果<=K,那么这个点对就称为weak,求有多少个这样的点对。

思路:

要求w[u]*w[v]<=k,那么到v的时候,所有小于等于k/w[v]的u都满足,可以想到树状数组(光是DFS会T)。结点的值最大10亿,肯定要离散化,离散化的时候要把k/w[v]加进去一起离散,还要注意权值ai可以为0,这个只要把大于1e5的任一个数加进去a[]就好了第一个要求u 是v 的祖先,那么可以dfs,遍历到v时,要使上方的都是满足第一条件的u,即遍历某个节点u的子树时,这时u不可能是任何节点的祖先 删除u,这样就能保证所有有贡献的都是祖先。复杂度O(nlogn)

PS:大连区域赛网赛最后一题,做的时候只用了DFS,一直T,之前做过树状数组的题,不过做的少,意识还不够强_(:зゝ∠)_

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define ll __int64
using  namespace std;
const int N=1e5+10;
int vis[N];
int in[N];//in[i]为0的为root
int  bit[N<<1],n;
vector<int>e[N];
ll k,ans;
ll a[N<<1],li[N<<1];

int sum(int i){
  int s=0;
  while(i>0){
    s+=bit[i];
    i-=i&-i;
  }
  return s;
}

void add(int i,int x){
  while(i<=2*n){
    bit[i]+=x;
    i+=i&-i;
  }
}

void dfs(int rt){
  vis[rt]=1;
  ans+=sum(a[rt+n]);//在它之前进入的都为它的祖先
  add(a[rt], 1);
  for(int i=0; i<e[rt].size(); i++){
    int son=e[rt][i];
    if(!vis[son]){
      dfs(son);
    }
  }
  add(a[rt], -1);//该节点的子树全都遍历完了 该节点不是任何节点的祖先时 删除该节点的值
}

void lisan(ll x[]){//离散化模板
  for(int i=1; i<=2*n; i++)li[i]=x[i];
  sort(li+1, li+2*n+1);//unique的作用是“去掉”容器中相邻元素的重复元素,它实质上是一个伪去除,
  int m=unique(li+1, li+2*n+1)-li-1;//它会把重复的元素添加到容器末尾,而返回值是去重之后的尾地址
  for(int i=1; i<=2*n; i++)x[i]=lower_bound(li+1, li+m+1, x[i])-li;//一般使用前需要对容器进行排序
}//m也等于去重数组的大小

int  main(){
//  #ifndef ONLINE_JUDGE
//  freopen("in.txt","r",stdin);
//  #endif

  int t;
  scanf("%d",&t);
  while(t--){
    memset(bit, 0, sizeof(bit));
    memset(vis, 0, sizeof(vis));
    memset(in, 0, sizeof(in));
    ans=0;
    scanf("%d%I64d",&n,&k);
    for(int i=1; i<=n; i++){
      e[i].clear();
    }
    for(int i=1; i<=n; i++){
      scanf("%I64d",&a[i]);
      if(a[i]==0)a[i+n]=N;
      else a[i+n]=k/a[i];
    }
    lisan(a);//数据a[i]<=10^9 n<=10^5 数据离散化使a[i]的范围中10^5内
            //使得树状数组的空间不溢出
    for(int i=1; i<=n-1; i++){
      int u,v;
      scanf("%d%d",&u,&v);
      e[u].push_back(v);
      in[v]++;
    }
    int rt;
    for(int i=1; i<=n; i++){
      if(!in[i]){
        rt=i;break;
      }
    }
    dfs(rt);
    printf("%I64d\n",ans);
  }
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值