Lightoj 1128 greatest parent
这道题给出一个小根堆,输入v, k,要求求出树根和v节点之间的路径上,权值大于等于k的离树根最近的节点。由于儿子比父亲大,所以就可以用lca的思想,倍增法做这道题,预处理点权即可。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int maxn = 1e5 + 5; 5 int fa[maxn][22], val[maxn], head[maxn], to[maxn], nxt[maxn], n, cnt; 6 void add(int a, int b) 7 { 8 cnt++; 9 to[cnt] = b; 10 nxt[cnt] = head[a]; 11 head[a] = cnt; 12 } 13 void dfs(int x) 14 { 15 for(int i = 1; i <= 20; i++) 16 fa[x][i] = fa[fa[x][i-1]][i-1]; 17 for(int i = head[x]; i; i = nxt[i]) 18 dfs(to[i]); 19 } 20 int find(int x, int y) 21 { 22 for(int i = 20; i >= 0; i--) 23 if(val[fa[x][i]] >= y) 24 x = fa[x][i]; 25 return x; 26 } 27 int main() 28 { 29 int t, q, p, s, k, v; 30 scanf("%d", &t); 31 for(int j = 1; j <= t; j++) 32 { 33 cnt = 0; // cnt清零似乎很难想到,在这个地方re了好几次 34 memset(head, 0, sizeof(head)); 35 memset(to, 0, sizeof(to)); 36 memset(nxt, 0, sizeof(nxt)); 37 memset(val, 0, sizeof(val)); 38 memset(fa, 0, sizeof(fa)); 39 scanf("%d %d", &n, &q); 40 val[0] = 1; 41 for(int i = 1; i < n; i++) 42 { 43 scanf("%d %d", &p, &s); 44 add(p, i); 45 val[i] = s; 46 fa[i][0] = p; 47 } 48 dfs(0); 49 printf("Case %d: ", j); // 开始少打了这句,wa了,打上之后又wa了,发现少打了冒号…… 50 for(int i = 1; i <= q; i++) 51 { 52 scanf("%d %d", &k, &v); 53 printf("%d ", find(k, v)); 54 } 55 } 56 return 0; 57 }
跳跳棋
一道国家集训队的神题,为了方便描述,我们把左边的棋子称为a,中间的棋子称为b,右边的为c。仔细观察跳棋规则,我们会发现当左右两跳棋到中间距离不等时有三种转移方式(因为不能跳过两个棋子)
- b往a方向跳
- b往c方向跳
- a,c离b距离近的往里跳
a,c到b距离相等的时候只有1,2两种转移方式。