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

本文介绍了一种使用树状数组和深度优先搜索(DFS)解决特定树形结构问题的方法。该问题要求在给定一棵带有点权的树时,找到所有满足特定条件的节点对(u, v),其中u是v的祖先且两节点点权的乘积小于等于给定阈值K。通过树状数组维护路径上的点权倒数,快速统计符合条件的祖先节点数量。

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

题意:
n个节点的树,节点的点权为ai,要求找出有多少个二元组(u,v)满足
1:u是v的祖先且u!=v
2:a[u]*a[v]<=K

题解:

对于单个节点而言,可以讨论以这个节点为v,有多少祖先和其相乘满足条件,即算单点对答案的贡献度。

这题很容易就能转化成:

对单个节点而言,统计比当前节点大的祖先k/ai;

由此,可以用树状数组维护从根节点开始的k/ai值。

每遍历到一个节点,求出树状数组里比当前ai大的数的个数。

再将其的k/ai值植入树状数组。

还有一点遍历过的节点要及时删除。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int n,m;
typedef long long ll;
long long k;
ll val[N],b[N],f[N],tr[N];
vector<int> v[N];


#define lowbit(x) (x)&(-x)

int getid(long long x) {
    return lower_bound(b+1,b+m+1,x)-b;
}

void add(int x,int val)
{
    while(x<N-50)
    {
        tr[x]+=val;
        x+=lowbit(x);
    }
}

ll sum(int x)
{
    ll res=0;
    while(x>0)
    {
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}
ll res;
void dfs(int x)
{
    int h=(lower_bound(b+1,b+m+1,k/val[x]+1)-b)-1;//第一个大于等于k/val[x]的数再减1得到最后一个
    //得到最后一个小于等于 k/val[x] 的数
    res+=sum(h);
    add(getid(val[x]),1);
    for(int i=0;i<v[x].size();i++)
    {
        dfs(v[x][i]);
    }
    add(getid(val[x]),-1);
}


int main()
{
    int t;
    while(scanf("%d",&t)!=EOF)
    {
        while(t--)
        {
            memset(tr,0,sizeof(tr));
            scanf("%d%lld",&n,&k);
            for(int i=1;i<=n;i++)
            {
                v[i].clear();
                f[i]=0;
                scanf("%lld",&val[i]);
                b[i]=val[i];
            }
            sort(b+1,b+1+n);
            m=unique(b+1,b+1+n)-(b+1);
            for(int i=0;i<n-1;i++)
            {
                int c,d;
                scanf("%d%d",&c,&d);
                v[c].push_back(d);
                f[d]=1;
            }
            res=0;
            for(int i=1;i<=n;i++)
            {
                if(!f[i])
                {
                    dfs(i);
                    break;
                }
            }
            printf("%lld\n",res );
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值