树状数组+dfs(hdu 5877)

本文介绍了一种使用离散化处理和树状数组解决特定类型组合计数问题的方法,通过实例讲解了如何在给定一棵带根树的情况下,计算满足特定条件的节点对数量。

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5877

题意:给定一棵树(带根),定义weak pair 为1.u为v的祖先,2.a(u)*a(v)<=k,求这棵树总共有多少个这样的weak pair。输入说明:第一行输入测试样例的个数,第二行是n和k,n为树的节点数,k为限定值,接下来一行输入n个数,表示a(i),接下来n-1行表示各个点的连接,其中前面的点位父节点,后面的点位子节点

题解:先用map对a进行离散化处理,因为总共10W的点,但数据表示范围很大,树状数组完全存不下,所以要离散化,然后找出根结点,从根结点遍历子节点,每次遍历的时候先统计之前加入的满足a(u)<=k/a(v)的点的个数,即存在多少个weak pair,再将当前点加入树状数组,进行子节点的遍历,遍历完所有子节点后要进行删除当前节点,以后的节点不会再用到当前的节点,最后得出答案

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<vector>
#define INF 2e18
using namespace std;
long long value[200050];
int c[200050];
long long k;
int n;
long long sum;
long long v[200050];
map<long long,int> m;//用于离散化
vector<int> son[100050];//用于存储子节点
bool father[100050];//用于标记是否有父亲,主要为找到root
int lowbit(int x)
{
    return x & (-x);
}
void add(int x,int d)
{
    for(;x<=2*n+2;x+=lowbit(x))
    {
        c[x]+=d;
    }
}
int sumc(int x)
{
    int s=0;
    for(;x>0;x-=lowbit(x))
    {
        s+=c[x];
    }
    return s;
}
void dfs(int root)//遍历
{
    sum+=sumc(m[v[n+root]]);
    add(m[v[root]],1);//加入当前点
    for(int i=0;i<son[root].size();i++)
    {
        dfs(son[root][i]);

    }
    add(m[v[root]],-1);//去掉当前点
    son[root].clear();
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %lld",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&v[i]);
            if(v[i]) v[n+i]=k/v[i];
            else v[n+i]=INF;
            value[i]=v[i];value[n+i]=v[n+i];
        }
        sort(value+1,value+2*n+1);
        int cnt=1;
        for(int i=1;i<=2*n;i++)
        {
            if(!m[value[i]]) m[value[i]]=cnt++;
        }
        memset(father,0,sizeof(father));
        for(int i=1;i<=n-1;i++)
        {
            int f,s;
            scanf("%d %d",&f,&s);
            son[f].push_back(s);
            father[s]=1;
        }
        int root;
        for(int i=1;i<=n;i++)//找root
        {
            if(!father[i]){root=i;break;}
        }
        memset(c,0,sizeof(c));
        sum=0;
        dfs(root);
        printf("%lld\n",sum);
        m.clear();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值