hdu5877(离散化树状数组&dfs回溯)

本文介绍了一道关于树状数组与离散化的题目,主要内容包括如何利用树状数组来维护祖先节点的信息,并通过离散化处理大数据量的问题。通过对每个节点进行深度优先搜索,实现了对所有符合条件的节点对(u, v)的计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好题,今天又学习了一波儿。

题意:

n个节点的树,节点的点权为ai,要求找出有多少个二元组(u,v)满足

1:u是v的祖先且u!=v

2:a[u]*a[v]<=K

dfs访问到一个节点的时候看他的祖先节点有没有和他相乘小于K的,用树状数组维护他的祖先出现的元素,计算结果。访问到一个节点时,维护树状数组,回溯时删掉。

这题数据很大,所以要加离散化。 好题,好题。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 101000;
int n,num;
int du[maxn];
long long k;
long long da[maxn];
long long tree[maxn];
long long sum[maxn];
vector<int>G[maxn];
inline int lowbit(int i)
{
    return i&(-i);
}
inline void add(int i,int x)
{
    while(i<=num)
    {
        //printf("sdfdsdfs\n");
        sum[i]+=x;
        i += lowbit(i);
    }
}
inline long long query(int i)
{
    long long summ = 0;
    while(i>=1)
    {
        summ += sum[i];
        i -= lowbit(i);
    }
    return summ;
}
inline int find(long long a)
{
    return upper_bound(tree+1,tree+1+num,a)-tree-1;
}
long long ans = 0;
void dfs(int u)
{
    //printf("%d\n",u);
    add(find(da[u]),1);
    int len = G[u].size();
    for(int i=0;i<len;i++)
    {
        int v = G[u][i]; dfs(v);
    }
    add(find(da[u]),-1);
    long long tmp ;
    if(da[u]==0) tmp = tree[num]+1;
    else tmp = k/da[u];
    if(tmp>tree[num]) tmp = tree[num]+1;
    ans += query(find(tmp));
}
int main()
{
    int cases,u,v;
    scanf("%d",&cases);
    while(cases--)
    {
        scanf("%d%I64d",&n,&k);
        for(int i=1;i<=n;i++) {scanf("%I64d",&da[i]); tree[i] = da[i];}
        sort(tree+1,tree+n+1);
        num = 1;
        for(int i=2;i<=n;i++)
            if(tree[num]!=tree[i])
                tree[++num] = tree[i];
        //printf("%d\n",num);
        for(int i=1;i<=n;i++) G[i].clear();  memset(du,0,sizeof(du));
        for(int i=2;i<=n;i++)
        {
            scanf("%d%d",&u,&v);
            G[u].push_back(v); du[v] ++;
        }
        memset(sum,0,sizeof(sum));
        ans = 0ll;
        for(int i=1;i<=n;i++) if(du[i]==0) dfs(i);
        printf("%I64d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值