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 }