题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5877

题意:

weak pair的要求:

1.u是v的祖先(注意不一定是父亲)

2.val[u]*val[k] <=k;

题解:

1.将val(以及k/val)离散化,可用map 或者 用数组。 只要能将val与树状数组或线段树的下标形成映射就可以了。

2.从根节点开始搜索(题目中的树,不是线段树或树状数组的树),先统计当前节点与祖先能形成多少对weak pair,然后将其插入到树状数组或线段树中。

3.递归其子树。递归完子树后,再把当前节点从树状数组或线段树中删去。因为:根据递归的特性,如果不删除,这个值将会残留在c数组, 那么他的堂兄弟,堂叔伯,堂侄子等(后一步 递归的)会误认为这个值是他的祖先的。所以要及时删除。

类似的题(边查询边更新):http://blog.csdn.net/dolfamingo/article/details/71001021

树状数组(map离散)

#include<cstdio>//hdu5877 树状数组 map离散 dfs
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#define LL long long
#define INF 2e18 using namespace std; LL sval[200020], val[200020];
int n,fa[100010],c[200010];
map<LL,int> m;
vector<int> son[100010];
LL k,ans; int lowbit(int x)
{
    return x & (-x);
} void add(int x, int d)
{
    for(; x<=2*n; x += lowbit(x))
    {
        c[x] += d;
    }
} int sumc(int x)
{
    int s = 0;
    for(;x>0; x -= lowbit(x))
    {
        s += c[x];
    }
    return s;
} void dfs(int rt)//c数组中的下标与val[i],k/val[i]映射 且k/v[i]的下标-i的下边等于n(自己定)
{
    ans += sumc(m[val[n+rt]]);//统计<=k/val[rt]的个数,为什么不直接 m[k/val[rt]]? 因为val[rt]可能为0
    add(m[val[rt]],1);
    for(int i = 0; i<son[rt].size(); i++)
    {
        dfs(son[rt][i]);
    }
    add(m[val[rt]],-1);
} int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%lld",&n,&k);         for(int i = 1; i<=n; i++)
        {
            //存k/val[i]是因为 val[i]*(k/val[i])<= k. 到时可以直接从树状数组中统计<=k/val[i]的个数
            scanf("%lld",&val[i]);
            if(val[i])
                val[n+i] = k/val[i];
            else            //0为特殊情况
                val[n+i] = INF;
        }          //sval的作用是将v值按从小到大,一一与c数组的下标形成映射
        for(int i = 1; i<=2*n; i++)
            sval[i] = val[i];
        sort(sval+1,sval+2*n+1);         int cnt = 0;
        m.clear();
        for(int i = 1; i<=2*n; i++)
        {
            //map的作用是将v值与c数组的下标形成映射
            if(!m[sval[i]]) m[sval[i]] = ++cnt;
        }         for(int i = 1; i<=n; i++)
            fa[i] = 0, son[i].clear();
        for(int i = 1,u,v; i<n; i++)
        {
            scanf("%d%d",&u,&v);
            son[u].push_back(v);
            fa[v] = u;
        }         ans = 0;
        memset(c,0,sizeof(c));
        for(int i = 1; i<=n; i++)
        {
            if(!fa[i])
            {
                dfs(i);
                break;
            }
        }
        printf("%lld\n",ans);     }
    return 0;
}

线段树(map离散):

#include<cstdio>//hdu5877 线段树 map离散 dfs
#include<cstring>//注意区分题目的树和线段树的树
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#define LL long long
#define INF 2e18 using namespace std; int n,len;//n是题目给出的树的结点个数,len是线段树的线段长度。
LL k,ans;
LL val[200100],sval[200100];//val记录原始值,sval记录经过排序,删除重复操作的值,用于线段树的操作
int fa[100100],sum[800100];
vector<int>son[100100];
map<LL,int>m; int query(int root, int le, int ri, int x, int y)
{
if(x<=le && y>=ri)
return sum[root]; int mid = (le+ri)/2, ret = 0;
if(x<=mid) ret += query(root*2,le,mid,x,y);
if(y>=mid+1) ret += query(root*2+1,mid+1,ri,x,y);
return ret;
} void update(int root, int le, int ri, int pos, int d)
{
if(le==ri)
{
sum[root] += d;
return;
} int mid = (le+ri)/2;
if(pos<=mid) update(root*2,le,mid,pos,d);
else update(root*2+1,mid+1,ri,pos,d);
sum[root] = sum[root*2] + sum[root*2+1];
} void dfs(int rt)
{
int last = m[val[n+rt]];
int pos = m[val[rt]]; ans += query(1,1,len,1,last); update(1,1,len,pos,1);
for(int i = 0; i<son[rt].size(); i++)
{
dfs(son[rt][i]);
}
update(1,1,len,pos,-1);
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%lld",&n,&k);
for(int i = 1; i<=n; i++)
{
scanf("%lld",&val[i]);
if(val[i])
val[n+i] = k/val[i];
else
val[n+i] = INF;
} for(int i = 1; i<=2*n; i++)
sval[i] = val[i];
sort(sval+1,sval+2*n+1); m.clear();
len = 0;
for(int i = 1; i<=2*n; i++)
{
if(!m[sval[i]]) m[sval[i]] = ++len;
} for(int i = 1; i<=n; i++)
fa[i] = 0, son[i].clear();
for(int i = 1,u,v; i<n; i++)
{
scanf("%d%d",&u,&v);
son[u].push_back(v);
fa[v] = u;
} ans = 0;
memset(sum,0,sizeof(sum));
for(int i = 1; i<=n; i++)
{
if(!fa[i])
{
dfs(i);
break;
}
} printf("%lld\n",ans);
}
return 0;
}

线段树(数组离散):

#include<cstdio>//hdu5877 线段树 dfs 普通数组进行离散
#include<cstring>//注意区分题目的树和线段树的树
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<vector>
#define LL long long
#define INF 2e18
using namespace std; int n,m;//n是题目给出的树的结点个数,m是线段树的线段长度。
LL k,ans;
LL val[200100],sval[200100];//val记录原始值,sval记录经过排序,删除重复操作的值,用于线段树的操作
int fa[100100],sum[800100];
vector<int>son[100100]; int query(int root, int le, int ri, int x, int y)
{
if(x<=le && y>=ri)
return sum[root]; int mid = (le+ri)/2, ret = 0;
if(x<=mid) ret += query(root*2,le,mid,x,y);
if(y>=mid+1) ret += query(root*2+1,mid+1,ri,x,y);
return ret;
} void update(int root, int le, int ri, int pos, int d)
{
if(le==ri)
{
sum[root] += d;
return;
} int mid = (le+ri)/2;
if(pos<=mid) update(root*2,le,mid,pos,d);
else update(root*2+1,mid+1,ri,pos,d);
sum[root] = sum[root*2] + sum[root*2+1];
} void dfs(int rt)
{
int last = lower_bound(sval+1, sval+m+1,val[n+rt]) - sval;
int pos = lower_bound(sval+1,sval+m+1,val[rt]) - sval; ans += query(1,1,m,1,last); update(1,1,m,pos,1);
for(int i = 0; i<son[rt].size(); i++)
{
dfs(son[rt][i]);
}
update(1,1,m,pos,-1);
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%lld",&n,&k);
for(int i = 1; i<=n; i++)
{
scanf("%lld",&val[i]);
if(val[i])
val[n+i] = k/val[i];
else
val[n+i] = INF;
} for(int i = 1; i<=2*n; i++)
sval[i] = val[i];
sort(sval+1,sval+2*n+1);
m = unique(sval+1,sval+2*n+1) - (sval+1); for(int i = 1; i<=n; i++)
fa[i] = 0, son[i].clear();
for(int i = 1,u,v; i<n; i++)
{
scanf("%d%d",&u,&v);
son[u].push_back(v);
fa[v] = u;
} ans = 0;
memset(sum,0,sizeof(sum));
for(int i = 1; i<=n; i++)
{
if(!fa[i])
{
dfs(i);
break;
}
} printf("%lld\n",ans);
}
return 0;
}

HDU5877 Weak Pair dfs + 线段树/树状数组 + 离散化的更多相关文章

  1. hdu4605 树状数组+离散化+dfs

    Magic Ball Game Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  2. POJ 2299 【树状数组 离散化】

    题目链接:POJ 2299 Ultra-QuickSort Description In this problem, you have to analyze a particular sorting ...

  3. BZOJ_5055_膜法师_树状数组+离散化

    BZOJ_5055_膜法师_树状数组+离散化 Description 在经历过1e9次大型战争后的宇宙中现在还剩下n个完美维度, 现在来自多元宇宙的膜法师,想偷取其中的三个维度为伟大的长者续秒, 显然 ...

  4. 2016 大连网赛---Weak Pair(dfs+树状数组)

    题目链接 http://acm.split.hdu.edu.cn/showproblem.php?pid=5877 Problem Description You are given a rooted ...

  5. HDU - 5877 Weak Pair (dfs+树状数组)

    题目链接:Weak Pair 题意: 给出一颗有根树,如果有一对u,v,如果满足u是v的父节点且vec[u]×vec[v]<=k,则称这对结点是虚弱的,问这棵树中有几对虚弱的结点. 题解: 刚开 ...

  6. hdu 5877 Weak Pair dfs序+树状数组+离散化

    Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Prob ...

  7. HDU 5877 Weak Pair DFS + 树状数组 + 其实不用离散化

    http://acm.hdu.edu.cn/listproblem.php?vol=49 给定一颗树,然后对于每一个节点,找到它的任何一个祖先u,如果num[u] * num[v] <= k.则 ...

  8. 线段树合并 || 树状数组 || 离散化 || BZOJ 4756: [Usaco2017 Jan]Promotion Counting || Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

    题面:P3605 [USACO17JAN]Promotion Counting晋升者计数 题解:这是一道万能题,树状数组 || 主席树 || 线段树合并 || 莫队套分块 || 线段树 都可以写..记 ...

  9. LightOJ 1085(树状数组+离散化+DP,线段树)

    All Possible Increasing Subsequences Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format: ...

随机推荐

  1. GPP加密破解工具gpp-decrypt

    GPP加密破解工具gpp-decrypt   GPP是Group Policy Preferences(组策略首选项)的缩写,这是一个组策略实施工具.通过该工具,网络管理员可以实现更多的网络管理,如驱 ...

  2. Android中Drawable知识总结

    本文是学习<Android开发艺术探索>中Drawable章节之后的一个总结. 一.常见的Drawable种类介绍 Drawable类 xml标签 描述 BitmapDrawable 表示 ...

  3. win7 x64 dtrace

    1.下载WINDOW DTRACE 工具 https://github.com/prash-wghats/DTrace-win32 2.系统参数修改 bcdedit/set testsigning o ...

  4. debian6之eclipse和jdk安装

    安装JDK 目前最新的JDK版本是:Java SE Development Kit 7u5 下载地址:http://www.oracle.com/technetwork/java/javase/dow ...

  5. jquery怎么找到元素下面的第一个子元素

    <ul><li>11</li><li>22</li><li>33</li><li>44</li&g ...

  6. sshpass结合ssh和scp可以自动完成密码登录,无需手动输入密码

    使用方法: 1.sshpass -p 123456 ssh admin@1.1.1.1 "touch file"  远程创建文件file 2.sshpass -p 123456 s ...

  7. C++11 并发指南系列(转)

    本系列文章主要介绍 C++11 并发编程,计划分为 9 章介绍 C++11 的并发和多线程编程,分别如下: C++11 并发指南一(C++11 多线程初探)(本章计划 1-2 篇,已完成 1 篇) C ...

  8. linux系列之-—04 自动删除n天前日志【转】

    让Linux系统定时清理一些不需要的文件,日志很有必要 1. 删除文件命令: find 对应目录 -mtime +天数 -name "文件名" -exec rm -rf {} \; ...

  9. Python web 框架:web.py

    web.py 是一个Python 的web 框架,它简单而且功能强大.web.py 是公开的,无论用于什么用途都是没有限 制的. web.py 安装: pip install web.py 下面开始我 ...

  10. SDUT 1068-Number Steps(数学:直线)

    Number Steps Time Limit: 1000ms   Memory limit: 10000K  有疑问?点这里^_^ 题目描写叙述 Starting from point (0,0) ...