HDU - 5877 Weak Pair (dfs序+树状数组+离散化)

VJ地址

题意:给一个有根树给你,计算一下满足下列条件的序列对的数目
(1)u是v的祖先(不能是它自己)
(2)a[v]*a[u]<=k

思路:用DFS序分裂每一条链,使链上的点都是当前加入点的祖先,用树状数组维护,or线段树,因为是单点修改就用就树状数组了。 每次加入点前,计算小于等于k/a[v]点的数量。还有数据大小是1e9,但是只有1e5个点,要离散化处理。

ps:还有要自己找到根节点。

具体细节:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
#define INF 2e18
#define inf 0x3f3f3f3f
#define FILL(a,b) (memset(a,b,sizeof(a)))
#define re register
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(a) ((a)&-(a))
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
#define fi first
#define se second

using namespace std;
typedef long long  ll;
typedef unsigned long long  ull;
typedef pair<int,int > pii;
int dx[4]= {-1,1,0,0},dy[4]= {0,0,1,-1};
const ll mod=1e9+7;
const ll N =2e5+10;
const double eps = 1e-4;
const double pi=acos(-1);
ll gcd(int a,int b){return !b?a:gcd(b,a%b);}
using namespace std;
vector<int> g[N];
ll n,k;
ll a[N];
ll lsh[N];
ll sum;
ll cnt;
ll c[N];
int vis[N];
ll getid(ll x)
{
    return lower_bound(lsh+1,lsh+cnt+1,x)-lsh;
}
void add(ll x,ll v)
{
    while(x<=N){
        c[x]+=v;
        x+=lowbit(x);
    }
}
ll query(ll x)
{
    ll ans=0;
    while(x){
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
void dfs(int u,int p){
    add(getid(a[u]),1);
    for(int v:g[u]){
        if(v==p) continue;
        sum+=query(getid(k/a[v]+1)-1);
        dfs(v,u);
    }
    add(getid(a[u]),-1);
}
void sovle(){
    sum=0;
    cin>>n>>k;
    FILL(c,0);
    FILL(vis,0);
    for(int i=1;i<=n;i++) {
        cin>>a[i];
        lsh[i]=a[i];
    }
    sort(lsh+1,lsh+1+n);
    cnt=unique(lsh+1,lsh+n+1)-lsh-1;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
        vis[v]=1;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]) {dfs(i,0);break;}
    }
    cout<<sum<<endl;
    for(int i=1;i<=n;i++) g[i].clear();
}
int main()
{
    ios
    int t=1;
    cin>>t;
    while(t--){
        sovle();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值