您的位置:首页 > 其它

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
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: