树上二分

http://codeforces.com/contest/740/problem/D

相同的一道题目:http://codeforces.com/gym/101147/problem/J

读完题目,暴力的写法很容易写出来,对于每一个点,不断往父节点找,直到没有父节点,或者不满足条件直接跳出。就是这里:dis(u,v) <= a[v],这里,不断往上父节点,dis是严格单调递增的,同时a保持不变,所以可以考虑二分,我没做出来,我做的时候,也分析出这个性质,想到应该用sparse table,但是这个区间内的每个节点都要加1,我不知道怎么加快这个过程,那就是还要遍历,跟暴力的复杂度差不多,但是,确实有trick可以高效的实现!考虑区间端点+1和-1,这不跟统计线段的重叠最大次数的套路是一样的么,感觉自己还是不能灵活应用。然后从根节点开始,通过dfs收集所有儿子节点的和,就是最终的结果。比如 0 -1 0 1这条路径,最后的结果就是0, 0, 1, 1,路径上的1和-1合并,对这个区间外的值,没有影响。好像哪里也见过这个套路,好像是,统计区间内只出现一次的数的个数。套路,方法是一致的。

总结几点:

1. 看到单调的性质,或者这样(000000111或者1110000000)这样,1代表满足条件,0代表不满足,要立马想到二分。非常重要。

2. sparse table的数据结构要熟练的写出来。经典的应用是区间查询,本质还是二分。rmq。父节点的父节点,怎么二分,怎么找lca,都要搞清楚。lca的离线rmq算法。

3. 最后统计区间的个数的trick,+1, -1,还是没有分析清楚。后面需要进一步学习。

 

这道题目,还有其他的做法,就是变换条件dis(u,v) <= a[v], => dis(v) - dis(u) <= a[v] => dis(v) - a[v] <= dis(u) 这里v是u的儿子节点,构造dfs访问顺序,儿子节点的顺序在(in[u], out[u])之间,需要统计这个区间内满足上面条件的点的个数。好像一般统计小于某个值的节点个数,都是提前排好序,按照从小到大的顺序,然后查询的时候只需要查询区间的元素个数既可以,而区间查询可以采取线段树,树状数组来做(好像不知道怎么高效的查询区间内小于某一个值的元素的个数),然后就结束了。 一般多个变量,固定几个变量,寻找一个随意变量,而这个变量可以通过二分等logn高效的方法实现,来进行加速。

贴一下代码,这里贴的都是别人的代码,遇到类似的问题,可以直接拿出来使用。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <vector>
 5 #include <string>
 6 #include <cstring>
 7 #include <map>
 8 #include <set>
 9 #include <bitset>
10 #include <cassert>
11 #include <cstdlib>
12 #include <ctime>
13 #include <unordered_map>
14 using namespace std;
15 
16 #define PB(x) push_back(x)
17 #define SZ(x) ((int)(x).size())
18 #define ERR(x) cout << #x" = " << x << "  "
19 #define NL cout << endl
20 #define ALL(x) (x).begin(), (x).end()
21 #define X first
22 #define Y second
23 typedef long long llint;
24 
25 const int N = 200100, LOG = 21;
26 typedef pair<int,int> pii;
27 int n, a[N], par[N][LOG];
28 vector<pii> g[N];
29 llint dep[N];
30 int z[N], ans[N];
31 
32 inline void dfs(int v, int p, llint d){
33     dep[v] = d; par[v][0] = p;
34     for (pii p: g[v])
35         dfs(p.X, v, d + 1LL*p.Y);
36 }
37 
38 inline void solve(int u){
39     int v = u;
40     for (int j = LOG-1; j >= 0; j--)
41         if (par[v][j] != -1 && (dep[u] - dep[par[v][j]]) <= 1LL*a[u])
42             v = par[v][j];
43     z[u]++;
44     if (par[v][0] != -1) z[par[v][0]]--;
45 }
46 
47 inline int go(int v){
48     int res = z[v];
49     for (pii p: g[v]){
50         res += go(p.X);
51     }
52     return ans[v] = res;
53 }
54 
55 int main(){
56     scanf("%d", &n);
57     for (int i = 1; i <= n; i++)
58         scanf("%d", a+i);
59     int p, w;
60     for (int i = 1; i < n; i++){
61         scanf("%d%d", &p, &w);
62         g[p].PB(pii(i+1, w));
63     }
64     memset(par, -1, sizeof par);
65     dfs(1, -1, 0);
66     for (int j = 1; j < LOG; j++)
67         for (int i = 1; i <= n; i++)
68             par[i][j] = (par[i][j-1] == -1) ? -1 : par[par[i][j-1]][j-1];
69     for (int i = 1; i <= n; i++)
70         solve(i);
71     go(1);
72     for (int i = 1; i <= n; i++) printf("%d ", ans[i]-1);
73 
74      return 0;
75 }

第二种解法

 1 #include <bits/stdc++.h>
 2 using namespace std; 
 3 
 4 const int N=1e6;
 5 const int Mod=1e9+7;
 6 
 7 long long dis[N];
 8 int n,m,a[N],p[N],w[N],ans[N],in[N],out[N],ord;
 9 vector<int> E[N],W[N];
10 
11 void dfs(int x,long long y) {
12     dis[x]=y;
13     in[x]=++ord;
14     for(int i=0;i<E[x].size();i++) {
15         dfs(E[x][i],y+W[x][i]);
16     }    
17     out[x]=ord;
18 }
19 
20 struct Node {
21     int t,id;
22     long long v;
23     Node(){}
24     Node(int t1,int id1,long long v1) {
25         t=t1; id=id1; v=v1;
26     } 
27     bool operator < (const Node &a) const {
28         if(v==a.v) return t<a.t;
29         return v<a.v; 
30     }
31 }Q[N];
32 
33 int cnt,B[N];
34 
35 inline void Ins(int x) {
36     while(x<=n) {
37         B[x]++;
38         x+=x&-x;
39     }
40 }
41 
42 inline int ask(int x) {
43     int res=0;
44     while(x) {
45         res+=B[x];
46         x-=x&-x;
47     }
48     return res;
49 }
50 
51 int main() {
52     cin>>n;
53     for(int i=1;i<=n;i++) scanf("%d",a+i);
54     for(int i=2;i<=n;i++) {
55         scanf("%d%d",p+i,w+i);
56         E[p[i]].push_back(i);
57         W[p[i]].push_back(w[i]);
58     } //cout<<"/";
59     dfs(1,0); //cout<<"/";
60     for(int i=1;i<=n;i++) {
61         Q[++cnt]=Node(1,i,dis[i]-a[i]);
62         Q[++cnt]=Node(2,i,dis[i]);
63     }
64     sort(Q+1,Q+1+cnt);
65     for(int i=1;i<=cnt;i++) {
66         if(Q[i].t==1) {
67             Ins(in[Q[i].id]);
68         } else {
69             ans[Q[i].id]=ask(out[Q[i].id])-ask(in[Q[i].id]-1);
70         }
71     }
72     for(int i=1;i<=n;i++) printf("%d ",ans[i]-1);
73 }

 

转载于:https://www.cnblogs.com/y119777/p/6102126.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值