2016 Asia Regional Dalian Online 1010 Weak Pair

本文详细解析了HDU5877题目中关于寻找弱有序对的算法实现,采用离散化+DFS+树状数组的方法解决特定有根树上的有序对计数问题。

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

HDU 5877

【题意】

给你一棵有根树,一个定值k,以及树上每个结点的值a[i]

对于有序对(u,v),如果(1)u是v的祖先,且(2)a[u]*a[v]<=k,则称该有序对(u,v)是弱的

问树中有多少对有序对(u,v)是弱的

【类型】

离散化+dfs+树状数组


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <map>
using namespace std;

const int maxn = 100010;

#define LL long long

LL n,k,a[maxn],ans;
map<LL,LL> mp;
vector<LL> sons[maxn];
vector<LL> b;
LL tree[2*maxn];
bool findroots[maxn];
int root;
int lowbit(LL x)
{
    return x&-x;
}
void add(LL x,LL val)
{
    for(LL i = x;i<2*maxn;i += lowbit(i))
        tree[i] += val;
}
LL getx(LL x)
{
    LL sum = 0;
    for(LL i=x;i;i -= lowbit(i))
        sum += tree[i];
    return sum;
}
void init()
{
    ans =0;
    mp.clear();
   memset(a,0,sizeof(a));
   b.clear();
   memset(tree,0,sizeof(tree));
   memset(findroots,false,sizeof(findroots));
}
void dfs(LL u)
{
    //printf("dfs...\n");
    int sz = sons[u].size();
    for(int i=0;i<sz;i++)
    {
        if(a[sons[u][i]]<=k) ans += getx(mp[(LL)k/a[sons[u][i]]]);
        add(mp[a[sons[u][i]]],1);
        dfs(sons[u][i]);
        add(mp[a[sons[u][i]]],-1);
    }
}
void solve()
{

    scanf("%lld %lld",&n,&k);
    init();
    LL x,y;
    for(int i=1; i<=n; i++)
    {
        scanf("%lld",&a[i]);
        if(a[i]>k)
        {
            b.push_back(a[i]);
        }

        else
        {
           b.push_back(k/a[i]);
           b.push_back(a[i]);
        }
    }
    sort(b.begin(),b.end());
    b.erase(unique(b.begin(),b.end()),b.end());
    for(LL i=0;i<(LL)b.size();i++)
    {
        mp[b[i]] = i+1;
       // printf("   %lld %lld\n",b[i],i+1);
    }
    for(int i=0; i<n-1; i++)
    {
        scanf("%lld %lld",&x,&y);
        sons[x].push_back(y);
    }
   // cout << " what ..." << endl;
    for(int i=1;i<=n;i++)
    {
        int sz = sons[i].size();
        for(int j=0;j<sz;j++)
        {
            findroots[sons[i][j]] = true;
        }
    }
    //int root = 1;
    for(int i=1;i<=n;i++)
    {
        if(!findroots[i])
        {
            root = i;
            break;
        }
    }
    //printf("root = %lld\n",root);
    add(mp[a[root]],1);
     dfs(root);
    printf("%lld\n",ans);
    for(int i=0;i<=n;i++)
    {
        sons[i].clear();
    }
}

int main()
{

    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        solve();
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值