CodeForces - 293E Close Vertices(树上点分治 + 树状数组)
2017-08-09 18:03
288 查看
题意:
一棵树上 告诉你边权。
问你有多少点对满足的路上的权值之和 小于等于w, 边数小于等于L。
思路:
显然树上的点分治。
和男人八题一样, 只不过多加了一个边数小于等于L的限制。(一维限制变成二维限制)
做法肯定还是一样。
只不过在求经过重心满足要求点对时有点变化 (其余算法 看上一篇文章)。。
分析一下, 因为边权最大1e9, 边数最大1e5 , 因此, 边数这一个限制,我们可以用数据结构干掉。
因此按照边权w 从小到大排序。
初始状态 把所有的边数 加到 线段树里或者树状数组里, 然后双指针在移动时, 移动一个删一个。
双指针里肯定是边权符合要求的, 要想边数也符合, 直接数据结构查询即可。
注意:
分治的时候就不能在分治一次初始化数据结构一次了。 TLE了两发T_T
分析一下, 初始化数据结构肯定时o(nlogn)的 ,最后双指针肯定移动到一块去, 因此 最后数据结构里最多只有 最后指针那个位置不为空。 log n 删一下即可。
吐槽:
能用树状数组就别用线段树, 慢的两倍多= =!!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 100000 + 10;
int n, l;
long long w;
vector<pair<int,int> >g[maxn];
long long ans;
int vis[maxn];
int siz[maxn];
int mx[maxn];
///=======================
/// 树状数组
int c[maxn];
inline int lowbit(int x){
return x&-x;
}
int SUM(int x){
int ans = 0;
while(x){
ans += c[x];
x -= lowbit(x);
}
return ans;
}
void ADD(int x,int add){
while(x <= l+1){
c[x] += add;
x += lowbit(x);
}
}
///=======================
void getsize(int cur,int fa){
siz[cur] = 1;
mx[cur] = 0;
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
if (v != fa && !vis[v]){
getsize(v, cur);
siz[cur] += siz[v];
if (siz[v] > mx[cur]){
mx[cur] = siz[v];
}
}
}
}
int root, mi;
void find(int rt, int cur,int fa){ /// 找以rt 为根的树的重心。
mx[cur] = max(mx[cur], siz[rt] - siz[cur]);
if (mx[cur] < mi){
mi = mx[cur];
root = cur;
}
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
if (v != fa && !vis[v]){
find(rt, v, cur);
}
}
}
struct node{ /// 距离变成了一个二维的偏序关系。
long long d1;
int d2;
node(long long d1 = 0,int d2 = 0):d1(d1), d2(d2){}
};
bool cmp(node a, node b){ /// 按边权排序
return a.d1 < b.d1;
}
node dis[maxn];
int cnt;
void getdis(int cur,long long d1, int d2, int fa){
dis[cnt++] = node(d1, d2);
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
int w = g[cur][i].second;
if (v != fa && !vis[v]){
getdis(v, d1 + w, d2 + 1, cur);
}
}
}
long long solve(int cur,long long d1,int d2){
long long ret = 0;
cnt = 0;
getdis(cur, d1, d2, 0);
sort(dis, dis + cnt, cmp);
// memset(c,0,sizeof c);
int i = 0, j = cnt - 1;
for (int i = 0; i < cnt; ++i){
ADD(dis[i].d2+1, 1);
}
while(i < j){
ADD(dis[i].d2+1, -1);
while(i < j && dis[i].d1 + dis[j].d1 > w){
ADD(dis[j].d2+1, -1);
--j;
}
if (l - dis[i].d2 >= 0){
int tmp = SUM(l-dis[i].d2+1);
ret += tmp;
}
++i;
}
if (SUM(dis[j].d2 + 1))ADD(dis[j].d2+1,-1); /// *****只需要特判一下 就可以完全清空树状数组,不能memset, 会超时!!!
return ret;
}
const int inf = 0x3f3f3f3f;
void dfs(int cur){
getsize(cur, 0);
mi = inf;
find(cur, cur, 0);
ans += solve(root, 0, 0);
// printf("root = %d\n", root);
vis[root] = 1;
int tmproot = root;
for (int i = 0; i < g[tmproot].size(); ++i){
int v = g[tmproot][i].first;
int w = g[tmproot][i].second;
if (!vis[v]){
ans -= solve(v, w, 1);
dfs(v);
}
}
}
int main(){
scanf("%d %d %lld", &n, &l, &w);
for (int i = 1; i < n; ++i){
int x,y;
scanf("%d %d",&x, &y);
g[x].push_back(make_pair(i+1, y));
g[i+1].push_back(make_pair(x, y));
}
dfs(1);
printf("%lld\n", ans);
return 0;
}
E. Close Vertices
time limit per test
5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
You've got a weighted tree, consisting of n vertices. Each edge has a non-negative weight. The length of the path between any two vertices
of the tree is the number of edges in the path. The weight of the path is the total weight of all edges it contains.
Two vertices are close if there exists a path of length at most l between them and a path of weight at most w between
them. Count the number of pairs of vertices v, u (v < u),
such that vertices v and u are
close.
Input
The first line contains three integers n, l and w (1 ≤ n ≤ 105, 1 ≤ l ≤ n, 0 ≤ w ≤ 109).
The next n - 1 lines contain the descriptions of the tree edges. The i-th
line contains two integers pi, wi (1 ≤ pi < (i + 1), 0 ≤ wi ≤ 104),
that mean that the i-th edge connects vertex (i + 1)and pi and
has weight wi.
Consider the tree vertices indexed from 1 to n in some way.
Output
Print a single integer — the number of close pairs.
Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams
or the %I64dspecifier.
Examples
input
output
input
output
一棵树上 告诉你边权。
问你有多少点对满足的路上的权值之和 小于等于w, 边数小于等于L。
思路:
显然树上的点分治。
和男人八题一样, 只不过多加了一个边数小于等于L的限制。(一维限制变成二维限制)
做法肯定还是一样。
只不过在求经过重心满足要求点对时有点变化 (其余算法 看上一篇文章)。。
分析一下, 因为边权最大1e9, 边数最大1e5 , 因此, 边数这一个限制,我们可以用数据结构干掉。
因此按照边权w 从小到大排序。
初始状态 把所有的边数 加到 线段树里或者树状数组里, 然后双指针在移动时, 移动一个删一个。
双指针里肯定是边权符合要求的, 要想边数也符合, 直接数据结构查询即可。
注意:
分治的时候就不能在分治一次初始化数据结构一次了。 TLE了两发T_T
分析一下, 初始化数据结构肯定时o(nlogn)的 ,最后双指针肯定移动到一块去, 因此 最后数据结构里最多只有 最后指针那个位置不为空。 log n 删一下即可。
吐槽:
能用树状数组就别用线段树, 慢的两倍多= =!!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 100000 + 10;
int n, l;
long long w;
vector<pair<int,int> >g[maxn];
long long ans;
int vis[maxn];
int siz[maxn];
int mx[maxn];
///=======================
/// 树状数组
int c[maxn];
inline int lowbit(int x){
return x&-x;
}
int SUM(int x){
int ans = 0;
while(x){
ans += c[x];
x -= lowbit(x);
}
return ans;
}
void ADD(int x,int add){
while(x <= l+1){
c[x] += add;
x += lowbit(x);
}
}
///=======================
void getsize(int cur,int fa){
siz[cur] = 1;
mx[cur] = 0;
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
if (v != fa && !vis[v]){
getsize(v, cur);
siz[cur] += siz[v];
if (siz[v] > mx[cur]){
mx[cur] = siz[v];
}
}
}
}
int root, mi;
void find(int rt, int cur,int fa){ /// 找以rt 为根的树的重心。
mx[cur] = max(mx[cur], siz[rt] - siz[cur]);
if (mx[cur] < mi){
mi = mx[cur];
root = cur;
}
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
if (v != fa && !vis[v]){
find(rt, v, cur);
}
}
}
struct node{ /// 距离变成了一个二维的偏序关系。
long long d1;
int d2;
node(long long d1 = 0,int d2 = 0):d1(d1), d2(d2){}
};
bool cmp(node a, node b){ /// 按边权排序
return a.d1 < b.d1;
}
node dis[maxn];
int cnt;
void getdis(int cur,long long d1, int d2, int fa){
dis[cnt++] = node(d1, d2);
for (int i = 0; i < g[cur].size(); ++i){
int v = g[cur][i].first;
int w = g[cur][i].second;
if (v != fa && !vis[v]){
getdis(v, d1 + w, d2 + 1, cur);
}
}
}
long long solve(int cur,long long d1,int d2){
long long ret = 0;
cnt = 0;
getdis(cur, d1, d2, 0);
sort(dis, dis + cnt, cmp);
// memset(c,0,sizeof c);
int i = 0, j = cnt - 1;
for (int i = 0; i < cnt; ++i){
ADD(dis[i].d2+1, 1);
}
while(i < j){
ADD(dis[i].d2+1, -1);
while(i < j && dis[i].d1 + dis[j].d1 > w){
ADD(dis[j].d2+1, -1);
--j;
}
if (l - dis[i].d2 >= 0){
int tmp = SUM(l-dis[i].d2+1);
ret += tmp;
}
++i;
}
if (SUM(dis[j].d2 + 1))ADD(dis[j].d2+1,-1); /// *****只需要特判一下 就可以完全清空树状数组,不能memset, 会超时!!!
return ret;
}
const int inf = 0x3f3f3f3f;
void dfs(int cur){
getsize(cur, 0);
mi = inf;
find(cur, cur, 0);
ans += solve(root, 0, 0);
// printf("root = %d\n", root);
vis[root] = 1;
int tmproot = root;
for (int i = 0; i < g[tmproot].size(); ++i){
int v = g[tmproot][i].first;
int w = g[tmproot][i].second;
if (!vis[v]){
ans -= solve(v, w, 1);
dfs(v);
}
}
}
int main(){
scanf("%d %d %lld", &n, &l, &w);
for (int i = 1; i < n; ++i){
int x,y;
scanf("%d %d",&x, &y);
g[x].push_back(make_pair(i+1, y));
g[i+1].push_back(make_pair(x, y));
}
dfs(1);
printf("%lld\n", ans);
return 0;
}
E. Close Vertices
time limit per test
5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
You've got a weighted tree, consisting of n vertices. Each edge has a non-negative weight. The length of the path between any two vertices
of the tree is the number of edges in the path. The weight of the path is the total weight of all edges it contains.
Two vertices are close if there exists a path of length at most l between them and a path of weight at most w between
them. Count the number of pairs of vertices v, u (v < u),
such that vertices v and u are
close.
Input
The first line contains three integers n, l and w (1 ≤ n ≤ 105, 1 ≤ l ≤ n, 0 ≤ w ≤ 109).
The next n - 1 lines contain the descriptions of the tree edges. The i-th
line contains two integers pi, wi (1 ≤ pi < (i + 1), 0 ≤ wi ≤ 104),
that mean that the i-th edge connects vertex (i + 1)and pi and
has weight wi.
Consider the tree vertices indexed from 1 to n in some way.
Output
Print a single integer — the number of close pairs.
Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams
or the %I64dspecifier.
Examples
input
4 4 6 1 3 1 4 1 3
output
4
input
6 2 17 1 3 2 5 2 13 1 6 5 9
output
9
相关文章推荐
- codeforces293E (树上点分治+树状数组)
- 【codeforces】293E. Close Vertices 点分治+树状数组
- CodeForces - 669E cdq分治
- codeforces 448C Painting Fence(分治)
- Codeforces 490F Treeland Tour 树上的最长上升子序列
- 2013 多校第七场 hdu 4670 Cube number on a tree(树上点的分治)
- Codeforces 714D [树上启发式合并][Hash]
- CodeForces - 668D Little Artem and Time Machine(线段树||树状数组)
- codeforces 256 div2 C. Painting Fence 分治
- Codeforces 526F Pudding Monsters - CDQ分治 - 桶排序
- poj1741:Tree (树上点分治/treap+启发式合并)
- codeforces293E Close Vertices -- 点分治+树状数组
- [BZOJ1316]树上的询问 点分治
- Codeforces 526F 分治
- SPOJ 1825 FTOUR2 - Free tour II (树上点分治)
- codeforces 703D 树状数组 + 离线处理 + 离散化
- HDU 5324 (CDQ分治 树状数组)
- Codeforces 628E:Zbazi in Zeydabad 树状数组的奇妙用法
- Codeforces 533B 树上的dp(求最大偶数个节点的权重和)
- hdu 4918 Query on the subtree(树分治 + 树状数组)