Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)

Total Submission(s): 1637    Accepted Submission(s): 531

Problem Description
You are given a rooted tree
of N nodes,
labeled from 1 to N.
To the ith
node a non-negative value ai is
assigned.An ordered pair
of nodes (u,v) is
said to be weak if

  (1) u is
an ancestor of v (Note:
In this problem a node u is
not considered an ancestor of itself);

  (2) au×av≤k.



Can you find the number of weak pairs in the tree?
 
Input
There are multiple cases in the data set.

  The first line of input contains an integer T denoting
number of test cases.

  For each case, the first line contains two space-separated integers, N and k,
respectively.

  The second line contains N space-separated
integers, denoting a1 to aN.

  Each of the subsequent lines contains two space-separated integers defining an edge connecting nodes u and v ,
where node u is
the parent of node v.



  Constrains: 

  

  1≤N≤105 

  

  0≤ai≤109 

  

  0≤k≤1018
 
Output
For each test case, print a single integer on a single line denoting the number of weak pairs in the tree.
 
Sample Input
1
2 3
1 2
1 2
 
Sample Output
1
 
Source
 

【题解】

这道题的意思是,这两个条件同时满足才是一对weak pair,而不是说其中之一。

解法是,从根节点往下遍历树。

可以看到我们的遍历顺序是1->2->3.

然后我们把之前走过的点记在栈中(实际上dfs本身就是利用栈),然后走到3,之前的点就都是3的祖先了。

但是有个问题就是,当我们走到4之后。3也在栈中。而3不是4的祖先。于是我们要把3给退栈,实现方法如下。

void dfs(int x)

{

查找某种数据结构中小于等于k/a[x]的元素个数

把当前点加入到某种数据结构中

dfs(儿子);

把当前点从某种数据结构中退出来。

}

可以选用的数据结构

1.平衡树

平衡树的话应该就很好理解了吧。

就是把某个节点加入平衡树。查找小于m的值。然后把某个节点删掉。

这三个操作不会难。

(这里用的是SBT)

2.树状数组

因为ai过大。所以需要用到离散化的思想。

具体的实现方法:

seq[1..n]存a[1..n]

在seq[n+1..2*n]存k/a[i-n](i∈[n+1..2*n]),特殊的,如果a[i-n]==0,则seq[i] = INF.

然后把seq数组升序排。

然后当我们要找小于等于k/a[x]加入的时候。就在seq数组中索引k/a[x]的下标key。

因为k/a[x]也加进去了。所以如果要找小于等于k/a[x]的数则只要获取前缀和就可以了。

然后把a[x]加入,就索引seq中a[x]的下标key2.然后递增其值,然后修改与其相关的点。

把a[x]删去,就仍索引seq中a[x]的下标(其实已经索引过了就是key2),然后递减其值,再修改相关点。

3.线段树

仍然按照树状数组的方法离散化。然后就是把树状数组改成线段树了而已。单点递增。很容易的。直接递增答案query(1,key)即可;

【代码1】平衡树

#include <cstdio>
#include <cstring>
#include <vector> const int MAXN = 100009; using namespace std; int T, n, father[MAXN], root_tree, root;
int si_ze[MAXN * 2], l[MAXN * 2], r[MAXN * 2], totn = 0;
__int64 key[MAXN * 2], k, seq[MAXN], ans = 0;
vector <int> son[MAXN]; void init()
{
memset(father, 0, sizeof(father));
for (int i = 1; i <= 100000; i++)
son[i].clear();
root = 0;
totn = 0;
ans = 0;
} void input_data()
{
scanf("%d%I64d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%I64d", &seq[i]);
int u, v;
for (int i = 1; i <= n - 1; i++)
{
scanf("%d%d", &u, &v);
father[v] = u;
son[u].push_back(v);
}
for (int i = 1; i <= n; i++)//找根节点
if (father[i] == 0)
{
root_tree = i;
break;
}
} void right_rotation(int &t)//右旋
{
int k = l[t];
l[t] = r[k];
r[k] = t;
si_ze[k] = si_ze[t];
si_ze[t] = si_ze[l[t]] + si_ze[r[t]] + 1;
t = k;
} void left_rotation(int &t) //左旋
{
int k = r[t];
r[t] = l[k];
l[k] = t;
si_ze[k] = si_ze[t];
si_ze[t] = si_ze[l[t]] + si_ze[r[t]] + 1;
t = k;
} void maintain(int &t, bool flag)
{
if (flag)
{
if (si_ze[l[l[t]]] > si_ze[r[t]]) // 这是/型
right_rotation(t);
else
if (si_ze[r[l[t]]] > si_ze[r[t]])//....
{
left_rotation(l[t]);
right_rotation(t);
}
else
return;//是平衡的就结束
}
else
{
if (si_ze[r[r[t]]] > si_ze[l[t]])
left_rotation(t);
else
if (si_ze[l[r[t]]] > si_ze[l[t]])
{
right_rotation(r[t]);
left_rotation(t);
}
else
return;
}
maintain(l[t], true);
maintain(r[t], false);
maintain(t, true);
maintain(t, false);
} void insert(int &t, __int64 data2)
{
if (t == 0)
{
t = ++totn;
l[t] = r[t] = 0;
si_ze[t] = 1;
key[t] = data2;
}
else
{
si_ze[t]++;
if (data2 < key[t])
insert(l[t], data2);
else
insert(r[t], data2);
maintain(t, data2 < key[t]);
}
} void de_lete(__int64 data2, int &t)
{
si_ze[t]--;
if (data2 == key[t])
{
bool flag1 = true, flag2 = true;//用于判断有无左右子树。
if (l[t] == 0)
flag1 = false;
if (r[t] == 0)
flag2 = false;
if (!flag1 && !flag2)
t = 0;
else
if (!flag1 && flag2)
t = r[t];
else
if (flag1 && !flag2)
t = l[t];
else
if (flag1 && flag2)
{
int temp = r[t];
while (l[temp])
temp = l[temp];
key[t] = key[temp];
de_lete(key[temp], r[t]);
}
}
else
if (data2 < key[t])
de_lete(data2, l[t]);
else
de_lete(data2, r[t]);
} __int64 find(__int64 what, int &t)
{
if (t == 0)
return 0;
__int64 temp = 0;
if (what >= key[t]) //左子树加上这个节点都小于等于what满足要求
{
temp += si_ze[l[t]] + 1;
temp += find(what, r[t]);
}
else
return find(what, l[t]);
return temp;
} void dfs(int rt)
{
__int64 temp = k / seq[rt];
if (seq[rt] == 0)
temp = 2100000000;//这里temp本应改成1e19的,但是貌似也对了。。
ans += find(temp, root);
insert(root, seq[rt]);
int len = son[rt].size();
for (int i = 0; i <= len - 1; i++)
dfs(son[rt][i]);
de_lete(seq[rt], root);
} void output_ans()
{
printf("%I64d\n", ans);
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
//freopen("F:\\rush_out.txt", "w", stdout);
scanf("%d", &T);
while (T--)
{
init();
input_data();
dfs(root_tree);
output_ans();
}
return 0;
}

【代码2】树状数组

#include <cstdio>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 101000;
const long long INF = 10000000000000000000; int c[MAXN * 2], a[MAXN], father[MAXN], n, lena;
long long seq[MAXN * 2], ans = 0, k;
vector <int> son[MAXN]; void init()
{
ans = 0;
memset(c, 0, sizeof(c));
memset(father, 0, sizeof(father));
for (int i = 1; i <= 100010; i++)
son[i].clear();
} void input_data()
{
scanf("%d%I64d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
seq[i] = a[i];
}
for (int i = n + 1; i <= 2 * n; i++)
{
long long temp = a[i - n];
if (a[i - n] == 0) //除0就改为无限大
seq[i] = INF;
else
seq[i] = k / temp;
}
sort(seq + 1, seq + 1 + n * 2);
//可能有重复的,但没关系,lower_bound返回的是一个特定值(seq是不会发生变化的)
lena = 2 * n;
} int lowbit(int x)
{
return x & -x;
} void dfs(int x)
{
int key;
if (a[x] == 0)
key = lower_bound(seq + 1, seq + 1 + 2 * n, INF) - seq;
else
key = lower_bound(seq + 1, seq + 1 + 2 * n, k / a[x]) - seq;
long long temp2 = 0;
while (key)
{
temp2 += c[key];
key = key - lowbit(key);
}
ans += temp2;
key = lower_bound(seq + 1, seq + 1 + 2 * n, a[x]) - seq;
int temp = key;
while (temp <= lena)
{
c[temp]++;
temp += lowbit(temp);
}
int len = son[x].size();
for (int i = 0; i <= len - 1; i++)
dfs(son[x][i]);
while (key <= lena)
{
c[key]--;
key += lowbit(key);
}
} void get_ans()
{
int root = 1;
for (int i = 1; i <= n - 1; i++)
{
int u, v;
scanf("%d %d", &u, &v);
father[v] = u;
son[u].push_back(v);
}
for (int i = 1; i <= n; i++)
if (father[i] == 0)
{
root = i;
}
dfs(root);
} void output_ans()
{
printf("%I64d\n", ans);
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
//freopen("F:\\rush_out.txt", "w", stdout);
int t;
scanf("%d", &t);
while (t--)
{
init();
input_data();
get_ans();
output_ans();
}
return 0;
}

【代码3】线段树,基础的单点递增

#include <cstdio>
#include <algorithm>
#include <vector>
#define lson begin, m, rt << 1
#define rson m+1,end,rt<<1|1 using namespace std; const int MAXN = 101000;
const long long INF = 10000000000000000000; int a[MAXN], father[MAXN], n, lena, sum[MAXN * 2 * 4]; //记住要开4倍
long long seq[MAXN * 2], ans = 0, k;
vector <int> son[MAXN]; void build(int begin, int end, int rt)
{
sum[rt] = 0;
if (begin == end)
return;
int m = (begin + end) >> 1;
build(lson);
build(rson);
} void init()
{
ans = 0;
memset(father, 0, sizeof(father));
for (int i = 1; i <= 100010; i++)
son[i].clear();
} void input_data()
{
scanf("%d%I64d", &n, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
seq[i] = a[i];
}
for (int i = n + 1; i <= 2 * n; i++)
{
long long temp = a[i - n];
if (a[i - n] == 0)
seq[i] = INF;
else
seq[i] = k / temp;
}
sort(seq + 1, seq + 1 + n * 2);
lena = 2 * n;
build(1, lena, 1);
} long long query(int l, int r, int begin, int end, int rt)//求区间和。
{
if (l <= begin && end <= r)
return sum[rt];
int m = (begin + end) >> 1;
long long temp = 0;
if (l <= m)
temp += query(l, r, lson);
if (m < r)
temp += query(l, r, rson);
return temp;
} void push_up(int rt)
{
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
} void updata(int pos, int num, int begin, int end, int rt)
{
if (begin == end)
{
sum[rt] += num;
return;
}
int m = (begin + end) >> 1;
if (pos <= m)
updata(pos, num, lson);
else
updata(pos, num, rson);
push_up(rt);
} void dfs(int x)
{
int key;
if (a[x] == 0)
key = lower_bound(seq + 1, seq + 1 + 2 * n, INF) - seq;
else
key = lower_bound(seq + 1, seq + 1 + 2 * n, k / a[x]) - seq;
long long temp2 = query(1, key, 1, lena, 1);
ans += temp2;
key = lower_bound(seq + 1, seq + 1 + 2 * n, a[x]) - seq;
updata(key, 1, 1, lena, 1);
int len = son[x].size();
for (int i = 0; i <= len - 1; i++)
dfs(son[x][i]);
updata(key, -1, 1, lena, 1);
} void get_ans()
{
int root = 1;
for (int i = 1; i <= n - 1; i++)
{
int u, v;
scanf("%d %d", &u, &v);
father[v] = u;
son[u].push_back(v);
}
for (int i = 1; i <= n; i++)
if (father[i] == 0)
{
root = i;
}
dfs(root);
} void output_ans()
{
printf("%I64d\n", ans);
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
//freopen("F:\\rush_out.txt", "w", stdout);
int t;
scanf("%d", &t);
while (t--)
{
init();
input_data();
get_ans();
output_ans();
}
return 0;
}

【33.18%】【hdu 5877】Weak Pair (3种解法)的更多相关文章

  1. HDU 5877 Weak Pair(弱点对)

    HDU 5877 Weak Pair(弱点对) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Jav ...

  2. 树形DP+树状数组 HDU 5877 Weak Pair

    //树形DP+树状数组 HDU 5877 Weak Pair // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 // 这道题要离散化 #i ...

  3. hdu 5877 Weak Pair (Treap)

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=5877 题面; Weak Pair Time Limit: 4000/2000 MS (Java/Other ...

  4. 2016 ACM/ICPC Asia Regional Dalian Online HDU 5877 Weak Pair treap + dfs序

    Weak Pair Problem Description   You are given a rooted tree of N nodes, labeled from 1 to N. To the  ...

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

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

  6. HDU 5877 Weak Pair (2016年大连网络赛 J dfs+反向思维)

    正难则反的思想还是不能灵活应用啊 题意:给你n个点,每个点有一个权值,接着是n-1有向条边形成一颗有根树,问你有多少对点的权值乘积小于等于给定的值k,其中这对点必须是孩子节点与祖先的关系 我们反向思考 ...

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

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

  8. HDU 5877 Weak Pair(树状数组)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5877 [题目大意] 给出一棵带权有根树,询问有几对存在祖先关系的点对满足权值相乘小于等于k. [题 ...

  9. HDU 5877 Weak Pair(树状数组+dfs+离散化)

    http://acm.hdu.edu.cn/showproblem.php?pid=5877 题意: 给出一棵树,每个顶点都有权值,现在要你找出满足要求的点对(u,v)数,u是v的祖先并且a[u]*a ...

随机推荐

  1. ImmutableMap不可使用null的问题

    示例 在项目中有发现类似下方的代码, Map tmpParams = ImmutableMap.of( "extraInfos", ext.get("extraInfos ...

  2. 【记录Bug】 This is probably not a problem with npm. There is likely additional logging output above.

    一个eslint的错误 我的报错如下 $ npm install > node-sass@4.11.0 install C:\Users\Administrator\Desktop\forGit ...

  3. python批量导出项目依赖包及批量安装的方法

    在Python中我们在项目中会用到各种库,自带的自然不必再说,然而如果是三方库,则在进行项目移植时通常需要在新的环境下安装需要的三方库文件,面对较大项目中众多的三方库,可以先将项目依赖库导出到txt文 ...

  4. 【风马一族_php】NO2_php基础知识

    原文来自:http://www.cnblogs.com/sows/p/5995763.html (博客园的)风马一族 侵犯版本,后果自负 回顾 什么是php以及php的发展史 搭建web服务器 apa ...

  5. 【时光回溯】【JZOJ3571】【GDKOI2014】内存分配

    题目描述 输入 输出 输出m行,每行一个整数,代表输入中每次程序变化后系统所需要的空闲内存单位数. 样例输入 2 3 1 4 1 4 2 2 1 2 1 1 1 1 1 样例输出 2 3 1 数据范围 ...

  6. UVA_10300:Ecological Premium

    Sample Input 351 1 12 2 23 3 32 3 48 9 239 1 86 12 18 1 1310 30 409 8 5100 1000 70Sample Output 3886 ...

  7. Libevent:1前言

    一:libevent概述: libevent是一个用来编写快速.可移植.非阻塞IO程序的库,它的设计目标是:可移植性.高效.可扩展性.便捷. libevent包含下列组件: evutil:对不同平台下 ...

  8. html的select标签清空option!~~~~

    最好的方法:document.getElementById("selectId").length = 1; 也可以document.getElementById("sel ...

  9. jenkins集成错误 标签: 发布 2016-01-10 20:45 747人阅读 评论(21) 收藏

    进入ITOO的项目以后,终于要将自己负责的模块在jenkins上面集成发布了.首先自己按照文档要求一步一步的将配置完成,然后构建,不错所料出错了,经过修改,终于构建成功!构建成功以后就没再管了,结果第 ...

  10. POJ-3616_Milking Time

    Milking Time Time Limit: 1000MS Memory Limit: 65536K Description Bessie is such a hard-working cow. ...