题目链接: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. spring mvc利用MultipartResolver解析Multipart/form-data进行文件上传

    之前的表单数据都是文本数据,现记录:利用MultipartResolver进行文件上传. ①首先,需引入commons-fileUpload和commons-io jar包,pom.xml文件的坐标: ...

  2. 网络流24T 太空飞行计划问题

    题目背景 题目描述 W 教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的 ...

  3. Redis - 事务操作与详解

    https://blog.csdn.net/J080624/article/details/81669560   写的比较清楚的一个帖子

  4. Swift:闭包(Closures)

    一. 基本概念 闭包(Closures)是自包括的功能代码块,能够在代码中使用或者用来作为參数传值. 在Swift中的闭包与C.OC中的blocks和其他编程语言(如C#)中的lambda, java ...

  5. 手把手教你如何利用Meterpreter渗透Windows系统

    在这篇文章中,我们将跟大家介绍如何使用Meterpreter来收集目标Windows系统中的信息,获取用户凭证,创建我们自己的账号,启用远程桌面,进行屏幕截图,以及获取用户键盘记录等等. 相关Payl ...

  6. java实验8-Java输入输出流

    1 读写文件 [实验目的] (1)掌握文本文件的读写方法. (2)掌握随机文件的读写方法. (3)掌握InputStream.OutputStream抽象类的基本使用. (4)掌握FileInputS ...

  7. 程序猿的量化交易之路(30)--Cointrader之ConfigUtil(17)

    转载须注明出处:viewmode=contents">http://blog.csdn.net/minimicall?viewmode=contents.http://cloudtra ...

  8. Solaris 目录与文件管理

    熟悉系统目录结构 掌握27个常用命令 掌握针对目录.文件的操作 掌握查找与文件内容的操作 一.命令 命令:内部命令(不依赖其他文件,可以直接执行)与外部命令 .他是用于实现某一类功能的指令或程序,其执 ...

  9. 简单的看Unicode和UTF-8的区别

    作者:uuspider链接:http://www.zhihu.com/question/23374078/answer/65352538来源:知乎著作权归作者所有,转载请联系作者获得授权. 举一个例子 ...

  10. 每天一个linux命令(23):Linux 目录结构(转)

    对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解linux文件目录结构的标准和每个目录的详细功能,对于我们用好linux系统只管重要,下面 ...