HDU 5877 dfs+离散化+树状数组(树上维护)

来源:互联网 发布:数据库源码 编辑:程序博客网 时间:2024/06/06 05:33

题意:

 给出一棵n个结点的树和一个数k, 每个节点上有权值​​, 问有多少个有序对(u,v)  (u,v)满足uv的祖先, a[u] * a[v] <=K;

思路:

首先这是在树上进行操作,然后就是找满足祖先关系,并且a[u] * a[v] <=K; 的点的个数。

找点的a[u] * a[v] <=K,可以枚举每一个点然后查找他的孩子中的值小于等于k/a[u] 的点的个数,最后全部统计起来就行了。

然后这就转化成找树上点有多少个小于某个值得问题了,对于这种问题要转换成树状数组进行维护,时间复杂度就变成了n*log(n)了。

#include<bits/stdc++.h>#define LL long long#define bug puts("**********")using namespace std;const int N=110000;LL k,a[N],sum[N],tree[N];int num=0,n;            ///离散化下标LL ans=0;int in[N];vector<int>vec[N];int lowbit(int x){    return x&(-x);}void add(int x,int d){    while(x<=n){        sum[x]+=d;        x+=lowbit(x);    }}LL getsum(int x){    LL tmp=0;    while(x){        tmp+=sum[x];        x-=lowbit(x);    }    return tmp;}int Find(LL x){    return upper_bound(tree+1,tree+1+num,x)-tree-1;    ///别忘了减一}void dfs(int u){    add(Find(a[u]),1);        ///表示这个结点出现过1次了(同时树状数组记录了前面出现过的 比这个结点小的 点的个数)    int len=vec[u].size();    for(int i=0;i<len;i++) dfs(vec[u][i]);    add(Find(a[u]),-1);                    ///防止影响到 不是祖先关系的其他结点(类似回溯,开始遍历u的兄弟结点)    LL tmp;    if(a[u]==0){        tmp=tree[num]+1;    }    else{        tmp=k/a[u];    }    if(tmp>tree[num])tmp=tree[num]+1;    ans+=getsum(Find(tmp));}int main(){    int t,u,v;    scanf("%d",&t);    while(t--){        ans=0;        scanf("%d%lld",&n,&k);        for(int i=1;i<=n;i++){            scanf("%lld",&a[i]);            tree[i]=a[i];        }        sort(tree+1,tree+n+1);        memset(sum,0,sizeof(sum));        memset(vec,0,sizeof(vec));        memset(in,0,sizeof(in));        num=1;        for(int i=2;i<=n;i++){          ///去重            if(tree[num]!=tree[i]){                tree[++num]=tree[i];            }        }        for(int i=0;i<n-1;i++){            scanf("%d%d",&u,&v);            vec[u].push_back(v);            in[v]++;        }        for(int i=1;i<=n;i++){            if(!in[i]){                dfs(i);break;            }        }        printf("%lld\n",ans);    }}


0 0
原创粉丝点击