ACM ICPC, Damascus University Collegiate Programming Contest(2018) Solution
A:Martadella Stikes Again
水。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define ll long long 6 7 int t; 8 ll R, r; 9 10 int main() 11 { 12 scanf("%d", &t); 13 while (t--) 14 { 15 scanf("%lld%lld", &R, &r); 16 if (R * R > 2ll * r * r) 17 puts("1"); 18 else 19 puts("2"); 20 } 21 return 0; 22 }
B:Amer and Graphs
题意:给出n条边,连续选k条边,(1 <= k <= n) 对于每一个图,有多少个图和它一样
思路:图Hash,枚举起点,再枚举长度,这样每次加边都是一条,时间复杂度O(n ^ 2)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INF 0x3f3f3f3f 5 #define INFLL 0x3f3f3f3f3f3f3f3f 6 #define ll long long 7 #define N 2003 8 9 typedef pair <int, int> pii; 10 11 struct simplehash 12 { 13 int len; 14 ll base, mod; 15 vector <ll> P, H; 16 17 inline simplehash() {} 18 inline simplehash(const int *ar, int n, ll b, ll m) 19 { 20 len = n; base = b, mod = m; 21 P.resize(len + 3, 1); H.resize(len + 3, 0); 22 for (int i = 1; i <= len; ++i) P[i] = (P[i - 1] * base) % mod; 23 for (int i = 1; i <= len; ++i) H[i] = (H[i - 1] + P[ar[i]]) % mod; 24 } 25 26 inline ll range_hash(int l, int r) 27 { 28 ll hashval = (H[r] - H[l - 1]) % mod; 29 return (hashval < 0 ? hashval + mod : hashval); 30 } 31 }; 32 33 struct arrayhash 34 { 35 simplehash sh1, sh2; 36 inline arrayhash() {} 37 inline arrayhash(const int *ar, int n) 38 { 39 sh1 = simplehash(ar, n, 1949313259, 2091573227); 40 sh2 = simplehash(ar, n, 1997293877, 2117566807); 41 } 42 inline ll range_hash(int l, int r) 43 { 44 return (sh1.range_hash(l, r) << 32) ^ (sh2.range_hash(l, r)); 45 } 46 }; 47 48 int t, n, pos; 49 map <pii, int> mp; 50 unordered_map <ll, int> mp2; 51 int arr[N]; 52 53 inline void Init() 54 { 55 mp.clear(); pos = 0; 56 mp2.clear(); 57 } 58 59 struct Edge 60 { 61 int u, v, id; 62 inline void scan() 63 { 64 scanf("%d%d", &u, &v); 65 if (u > v) swap(u, v); 66 if (!mp.count(pii(u, v))) 67 mp[pii(u, v)] = mp.size() + 1; 68 id = mp[pii(u, v)]; 69 } 70 }edge[N]; 71 72 inline void Run() 73 { 74 scanf("%d", &t); 75 while (t--) 76 { 77 scanf("%d", &n); Init(); 78 for (int i = 1; i <= n; ++i) 79 { 80 edge[i].scan(); 81 arr[i] = edge[i].id; 82 } 83 ll ans = 0; 84 arrayhash x = arrayhash(arr, n); 85 for (int i = 1; i <= n; ++i) 86 { 87 for (int j = i; j <= n; ++j) 88 { 89 ll Hash = x.range_hash(i, j); 90 ans += mp2[Hash]++; 91 } 92 } 93 printf("%lld\n", ans); 94 } 95 } 96 97 int main() 98 { 99 #ifdef LOCAL 100 freopen("Test.in", "r", stdin); 101 #endif 102 103 Run(); 104 105 return 0; 106 }
C:Help Shahhoud
题意:给出A串和B串,每次可以翻转以A串中心点为轴,翻转长度为x,求长度最少使得串A变成串B,如果不行输出-1
思路:很显然要从外往里翻,如果某一段翻转性质相同,那么可以一并翻转,要特别考虑一下四个字符都相同,那么既可以翻转既可以不翻转
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 #define N 100010 6 7 int t; 8 char A[N]; 9 char B[N]; 10 11 int main() 12 { 13 scanf("%d",&t); 14 while(t--) 15 { 16 scanf("%s", A + 1); 17 scanf("%s", B + 1); 18 int len = strlen(A + 1); 19 int ans = 0; 20 int flag = 0; 21 for(int i = 1; i <= len / 2; ++i) 22 { 23 int l = i, r = len - i + 1; 24 if(A[l] == A[r] && A[l] == B[r] && B[l] == B[r]) continue; 25 else if(A[l] == B[r] && A[r] == B[l]) 26 { 27 if(flag == 0) 28 { 29 ++ans; 30 flag = !flag; 31 } 32 } 33 else if(A[l] == B[l] && A[r] == B[r]) 34 { 35 if(flag == 1) 36 { 37 ++ans; 38 flag = !flag; 39 } 40 } 41 else 42 { 43 ans = -1; 44 break; 45 } 46 } 47 if(A[(len + 1) / 2] != B[(len + 1) / 2]) ans = -1; 48 printf("%d\n",ans); 49 } 50 return 0; 51 }
D:Simplified 2048
留坑。
E:Floods
题意:给出n个点形成山地(折线图),在下过一段时间的雨后留下的雨量。
思路:显然只有凹下去的点才能做出贡献。所以可以分为以下几种情况。一种是图中一部分,一种是图中二部分。对于一部分即求一个梯形面积,对于第二部分即求一个三角形的相似三角形的面积,累加一下即可。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 #define N 100010 6 7 struct node{ 8 int x,y; 9 inline node(){} 10 inline node(int x,int y) :x(x),y(y){} 11 }a[N]; 12 13 int L[N]; 14 int R[N]; 15 int n; 16 17 int main() 18 { 19 int t; 20 scanf("%d",&t); 21 while(t--) 22 { 23 scanf("%d",&n); 24 for(int i = 1; i <= n; ++i) 25 { 26 scanf("%d %d",&a[i].x,&a[i].y); 27 } 28 int Max = 0; 29 for(int i = 1; i <= n; ++i) 30 { 31 Max = max(Max,a[i].y); 32 L[i] = Max; 33 } 34 Max = 0; 35 for(int i = n; i >= 1; --i) 36 { 37 Max = max(Max, a[i].y); 38 R[i] = Max; 39 } 40 double ans = 0; 41 for(int i = 1; i < n; ++i) 42 { 43 int top, Max, Min; 44 if(a[i].y < a[i + 1].y) 45 { 46 top = min(L[i], R[i]); 47 Max = a[i + 1].y; 48 Min = a[i].y; 49 } 50 else 51 { 52 top = min(L[i + 1], R[i + 1]); 53 Max = a[i].y; 54 Min = a[i + 1].y; 55 } 56 if(top >= Max) 57 { 58 ans += (double)(a[i + 1].x - a[i].x) * (top - a[i].y + top - a[i + 1].y) * 0.5; 59 } 60 else if(top > Min) 61 { 62 ans += (double)(a[i + 1].x - a[i].x) * (top - Min) * (top - Min) / (Max - Min) * 0.5; 63 } 64 } 65 printf("%.8f\n",ans); 66 } 67 return 0; 68 }
F:Random Sort
题意:给出n个数,对标号全排列,看有多少种排列里面的数是排好的
思路:如果有相同的数,那么对于这个数它的贡献是fac[x] (fac是阶乘,x是个数)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int MOD = 7901; 6 7 #define N 1010 8 9 int inv[N]; 10 int n; 11 int a[N]; 12 13 int main() 14 { 15 inv[0] = 1; 16 for(int i = 1; i < N; ++i) 17 { 18 inv[i] = inv[i - 1] * i % MOD; 19 } 20 int t; 21 scanf("%d",&t); 22 while(t--) 23 { 24 scanf("%d",&n); 25 int ans = 1; 26 for(int i = 1; i <= n; ++i) 27 { 28 scanf("%d",&a[i]); 29 } 30 sort(a + 1, a + 1 + n); 31 int cnt = 1; 32 for(int i = 2;i <= n; ++i) 33 { 34 if(a[i - 1] == a[i]) 35 { 36 cnt++; 37 } 38 else 39 { 40 ans = ans * inv[cnt] % MOD; 41 cnt = 1; 42 } 43 } 44 ans = ans * inv[cnt] % MOD; 45 printf("%d\n",ans); 46 } 47 return 0; 48 }
G:Weird Requirements
题意:给出n个数,修改最少的数字,使得gcd=x,lcm=y。
思路:显然,一个数的因数不包括gcd和lcm的因数是一定要修改的。统计这些数字的个数。当剩下的数字的因数都满足gcd与lcm的因数的时候,显然最多修改两个数字。那么当必定修改的数的个数大于等于2的时候输出这个数字即可。对于剩下的数字,求出他们的gcd与lcm。那么y/gcd为需要增加的因数,lcm/x为需要减少的因数,但是当增加的因数和减少的因数的gcd!=1时需要修改两个数字。例如gcd=6,lcm=24,五个数分别为12 12 12 12 14
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define N 100010 6 7 typedef long long ll; 8 9 int t, n; 10 ll arr[N]; 11 ll x, y; 12 13 inline ll GCD(ll a, ll b) 14 { 15 return b == 0 ? a : GCD(b, a % b); 16 } 17 18 int main() 19 { 20 scanf("%d", &t); 21 while (t--) 22 { 23 scanf("%d", &n); 24 scanf("%lld%lld", &x, &y); 25 for (int i = 1; i <= n; ++i) scanf("%lld", arr + i); 26 if (y % x) 27 { 28 puts("-1"); 29 continue; 30 } 31 if(n == 1 && x != y) 32 { 33 puts("-1"); 34 continue; 35 } 36 int ans = 0; 37 ll gcd = y, lcm = x; 38 for (int i = 1; i <= n; ++i) 39 { 40 if(arr[i] % x || y % arr[i]) ++ans; 41 else 42 { 43 gcd = GCD(gcd, arr[i]); 44 lcm = lcm * arr[i] / GCD(lcm, arr[i]); 45 } 46 } 47 if (ans >= 2) 48 printf("%d\n", ans); 49 else if(ans == 1) 50 { 51 if(gcd != x && lcm != y && GCD(y / gcd, lcm / x) != 1) 52 { 53 puts("2"); 54 } 55 else 56 { 57 puts("1"); 58 } 59 } 60 else 61 { 62 if(x == gcd && y == lcm) 63 { 64 puts("0"); 65 } 66 else if(x != gcd && y != lcm && GCD(x / gcd, lcm / y) != 1) 67 { 68 puts("2"); 69 } 70 else 71 { 72 puts("1"); 73 } 74 } 75 } 76 return 0; 77 }
H:Shahhoud the Chief Judge
题意:一棵树,每个点有权值,sum = sgm(每个点的权值 * 路径经过它的次数)
思路:设cnt[i] 表示经过第i个点的路径数,如果存在gcd(cnt[i], cnt[j]) == 1 ,那么通过i, j 这两个数,就可以构造出所有的整数
裴蜀定理: ax + by = cgcd(a,b); c为任意整数,当gcd(a, b)==1 时 cgcd(a, b) 为任意整数
在这棵二叉树中,必然存在这两个数
考虑深度最深的叶子结点cnt[x] = 2 * n - 1, 那么它的父亲结点的cnt[fa] = 6 * n - 11
gcd(2n - 1, 6n - 11) = gcd(2n - 1, -8) = 1 因为2n - 1 是奇数
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INF 0x3f3f3f3f 5 #define INFLL 0x3f3f3f3f3f3f3f3f 6 #define ll long long 7 #define N 100010 8 9 inline ll GCD(ll a, ll b) 10 { 11 return (ll)b ? GCD(b, a % b) : a; 12 } 13 14 struct Edge 15 { 16 int to, nx; 17 inline Edge() {} 18 inline Edge(int to, int nx) : to(to), nx(nx) {} 19 }edge[N << 1]; 20 21 int head[N], pos; 22 int t, n; 23 ll arr[N]; 24 ll cnt[N]; 25 ll num[N]; 26 ll son[N][2]; 27 int fa[N]; 28 ll sum; 29 30 inline void Init() 31 { 32 memset(head, -1, sizeof head); 33 pos = 0; fa[1] = 1; sum = 0; 34 } 35 36 inline void addedge(int u, int v) 37 { 38 edge[++pos] = Edge(v, head[u]); head[u] = pos; 39 edge[++pos] = Edge(u, head[v]); head[v] = pos; 40 } 41 42 inline void DFS(int u) 43 { 44 cnt[u] = 1; 45 son[u][0] = son[u][1] = 0; 46 for (int it = head[u]; ~it; it = edge[it].nx) 47 { 48 int v = edge[it].to; 49 if (v == fa[u]) continue; 50 fa[v] = u; DFS(v); 51 cnt[u] += cnt[v]; 52 if (son[u][0]) 53 son[u][1] = cnt[v]; 54 else 55 son[u][0] = cnt[v]; 56 } 57 num[u] = (ll)(2 * n - 1) + (ll)(n - cnt[u]) * (cnt[u] - 1) * 2 + (ll)(son[u][0] * son[u][1] * 2); 58 sum += arr[u] * num[u]; 59 } 60 61 inline void work() 62 { 63 if (sum == 0) 64 { 65 puts("0"); 66 return; 67 } 68 int id = 1; 69 for (int i = 1; i <= n; ++i) 70 { 71 if (sum % num[i] == 0) 72 { 73 printf("1\n%d\n", i); 74 return; 75 } 76 if (GCD(num[i], num[fa[i]]) == 1) 77 id = i; 78 } 79 printf("2\n%d %d\n", fa[id], id); 80 } 81 82 inline void Run() 83 { 84 scanf("%d", &t); 85 while (t--) 86 { 87 scanf("%d", &n); 88 Init(); 89 for (int i = 1; i <= n; ++i) scanf("%lld", arr + i); 90 for (int i = 1, u, v; i < n; ++i) 91 { 92 scanf("%d%d", &u, &v); 93 addedge(u, v); 94 } 95 DFS(1); 96 work(); 97 } 98 } 99 100 int main() 101 { 102 #ifdef LOCAL 103 freopen("Test.in", "r", stdin); 104 #endif 105 106 Run(); 107 108 return 0; 109 }
I:lldar Yalalov
题意:n堆石子,每一堆有ai个,两个人轮流取,取的操作只有两种
1° 从一堆中取一个
2°每一堆取一个,当且仅当所有堆都至少有一个才能有这个操作
思路:
如果是奇数堆,那么取一个和从所有堆中取一个的操作实际上是一样的,因为取的都是奇数个,不会影响胜负 直接% 2 判断
如果是偶数堆,并且总个数是奇数,那么先手是必胜的,因为如果最小堆里面的石子个数是奇数个,那么只要一直取这里的,如果他取一排,跟着他取
如果总个数是偶数,并且最小堆里面石子个数是偶数,那么是胜利的,因为先手改变命运的次数多一次
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 #define INF 0x3f3f3f3f 5 #define N 110 6 7 int t; 8 int n, sum, Min; 9 int arr[N]; 10 11 inline bool work() 12 { 13 if (n & 1) 14 { 15 return (sum & 1); 16 } 17 if (sum & 1) return true; 18 else 19 { 20 return (Min & 1); 21 } 22 } 23 24 int main() 25 { 26 scanf("%d", &t); 27 while (t--) 28 { 29 scanf("%d", &n); 30 sum = 0, Min = INF; 31 for (int i = 1; i <= n; ++i) scanf("%d", arr + i), sum += arr[i], Min = min(Min, arr[i]); 32 puts(work() ? "Yalalov" : "Shin"); 33 } 34 return 0; 35 }
J:Saeed and Folan
水。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 int t, k; 6 int L[2], R[2], p[2], D[2]; 7 8 int main() 9 { 10 scanf("%d", &t); 11 while (t--) 12 { 13 for (int i = 0; i < 2; ++i) 14 scanf("%d%d%d%d", &L[i], &R[i], &p[i], &D[i]); 15 scanf("%d", &k); 16 for (int i = 0; i < 2; ++i) if (D[i] == 0) 17 D[i] = -1; 18 int ans = 0; 19 if (p[0] == p[1]) ++ans; 20 if (p[0] == L[0]) D[0] = 1; 21 if (p[0] == R[0]) D[0] = -1; 22 if (p[1] == L[1]) D[1] = 1; 23 if (p[1] == R[1]) D[1] = -1; 24 for (int i = 1; i <= k; ++i) 25 { 26 for (int j = 0; j < 2; ++j) 27 p[j] += D[j]; 28 if (p[0] == p[1]) ++ans; 29 for (int j = 0; j < 2; ++j) 30 { 31 if (p[j] == R[j]) D[j] = -1; 32 if (p[j] == L[j]) D[j] = 1; 33 } 34 } 35 printf("%d\n", ans); 36 } 37 return 0; 38 }
K:Another Shortest Path Problem
题意:n个点,n条边,没有重边和自环,每次询问给出u, v 询问u -> v的最短路径
思路:这个图是一棵树加一个环,那么我们找出这个环中边权最大的边
那么最短路只有两种状况,经过这条边和不经过这条边 然后找LCA处理一下
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 7 const int DEG = 20; 8 const int maxn = 1e5 + 10; 9 10 struct node{ 11 int u,v; 12 ll w; 13 inline node(){} 14 inline node(int u,int v,ll w): u(u), v(v), w(w){} 15 inline bool operator < (const node &b) 16 { 17 return w < b.w; 18 } 19 }G[maxn]; 20 21 struct Edge{ 22 int to,nxt; 23 ll w; 24 inline Edge(){} 25 inline Edge(int to,int nxt, ll w):to(to), nxt(nxt), w(w) {} 26 }edge[maxn << 1]; 27 28 int n,q; 29 int head[maxn], tot; 30 int father[maxn]; 31 ll dis[maxn]; 32 33 inline int find(int x) 34 { 35 return father[x] == x ? father[x] : father[x] = find(father[x]); 36 } 37 38 inline void mix(int x,int y) 39 { 40 x = find(x); 41 y = find(y); 42 if(x != y) 43 { 44 father[x] = y; 45 } 46 } 47 48 inline bool same(int x,int y) 49 { 50 return find(x) == find(y); 51 } 52 53 inline void addedge(int u,int v,ll w) 54 { 55 edge[tot] = Edge(v, head[u],w); 56 head[u] = tot++; 57 } 58 59 inline void init() 60 { 61 tot = 0; 62 memset(head, -1, sizeof head); 63 for(int i = 1; i <= n; ++i) father[i] = i; 64 } 65 66 int fa[maxn][DEG]; 67 int deg[maxn]; 68 69 inline void BFS(int root) 70 { 71 queue<int>q; 72 deg[root] = 0; 73 fa[root][0] = root; 74 q.push(root); 75 while(!q.empty()) 76 { 77 int tmp = q.front(); 78 q.pop(); 79 for(int i = 1;i < DEG; ++i) 80 { 81 fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1]; 82 } 83 for(int i = head[tmp]; ~i; i = edge[i].nxt) 84 { 85 int v = edge[i].to; 86 if(v == fa[tmp][0]) continue; 87 dis[v] = dis[tmp] + edge[i].w; 88 deg[v] = deg[tmp] + 1; 89 fa[v][0] = tmp; 90 q.push(v); 91 } 92 } 93 } 94 95 inline int LCA(int u, int v) 96 { 97 if(deg[u] > deg[v]) swap(u,v); 98 int hu = deg[u], hv = deg[v]; 99 int tu = u,tv = v; 100 for(int det = hv - hu, i = 0;det; det >>= 1, ++i) 101 { 102 if(det & 1) 103 { 104 tv = fa[tv][i]; 105 } 106 } 107 if(tu == tv) return tu; 108 for(int i = DEG - 1; i >= 0; --i) 109 { 110 if(fa[tu][i] == fa[tv][i]) continue; 111 tu = fa[tu][i]; 112 tv = fa[tv][i]; 113 } 114 return fa[tu][0]; 115 } 116 117 inline ll query(int u,int v) 118 { 119 int root = LCA(u,v); 120 return dis[u] + dis[v] - 2 * dis[root]; 121 } 122 123 int main() 124 { 125 int t; 126 scanf("%d",&t); 127 while(t--) 128 { 129 scanf("%d %d",&n,&q); 130 init(); 131 int a,b; 132 ll cost; 133 for(int i = 1; i <= n; ++i) 134 { 135 scanf("%d %d %lld", &G[i].u, &G[i].v, &G[i].w); 136 } 137 sort(G + 1, G + 1 + n); 138 for(int i = 1; i <= n; ++i) 139 { 140 int u = G[i].u; 141 int v = G[i].v; 142 ll w = G[i].w; 143 if(same(u, v)) 144 { 145 a = u; 146 b = v; 147 cost = w; 148 } 149 else 150 { 151 mix(u,v); 152 addedge(u,v,w); 153 addedge(v,u,w); 154 } 155 } 156 dis[1] = 0; 157 BFS(1); 158 while(q--) 159 { 160 int u,v; 161 scanf("%d %d",&u,&v); 162 ll ans = query(u, v); 163 ans = min(ans, query(u, a) + query(v, b) + cost); 164 ans = min(ans, query(v, a) + query(u, b) + cost); 165 printf("%lld\n",ans); 166 } 167 } 168 return 0; 169 }
L:V--o$\_$o--V
Upsolved.
题意:
$对每一个点i求下标小于i的所有点的LCA的点权和$
思路:
考虑一个点对多个点求分别的$LCA$ 可以树剖对那些点从当前点到根打标记,那么目标点对他们求$LCA就是从当前点走到根看一下打的标记最多的是哪个$
那么此处也可以同样处理,我们需要设计一种标记,使得从该点到根的路径上所有标记点加起来恰好是当前点权值
我们可以这么处理 在这里约定$w[u] 表示点u的点权,x[u] 表示树上的标记, fa[u] 表示点u的父亲$
那么 $x[u] = w[fa[u]] - w[i]$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 int t, n, w[N]; 7 vector <int> G[N]; 8 9 ll dis[N]; 10 int deep[N], fa[N], sze[N], son[N], top[N], p[N], fp[N], cnt; 11 void DFS(int u) 12 { 13 sze[u] = 1; 14 for (auto v : G[u]) 15 { 16 deep[v] = deep[u] + 1; 17 dis[v] = w[v] - w[u]; 18 DFS(v); sze[u] += sze[v]; 19 if (!son[u] || sze[v] > sze[son[u]]) son[u] = v; 20 } 21 } 22 23 void getpos(int u, int sp) 24 { 25 top[u] = sp; 26 p[u] = ++cnt; 27 fp[cnt] = u; 28 if (!son[u]) return; 29 getpos(son[u], sp); 30 for (auto v : G[u]) if (v != son[u]) 31 getpos(v, v); 32 } 33 34 namespace SEG 35 { 36 ll sum[N << 2], add[N << 2], lazy[N << 2]; 37 void build(int id, int l, int r) 38 { 39 sum[id] = lazy[id] = 0; 40 if (l == 1) sum[id] = dis[1]; 41 if (l == r) 42 { 43 add[id] = dis[fp[l]]; 44 return; 45 } 46 int mid = (l + r) >> 1; 47 build(id << 1, l, mid); 48 build(id << 1 | 1, mid + 1, r); 49 add[id] = add[id << 1] + add[id << 1 | 1]; 50 } 51 void work(int id, int l, int r, int ql, int qr, ll &res) 52 { 53 if (l >= ql && r <= qr) 54 { 55 res += sum[id]; 56 sum[id] += add[id]; 57 ++lazy[id]; 58 return; 59 } 60 if (lazy[id]) 61 { 62 lazy[id << 1] += lazy[id]; 63 sum[id << 1] += lazy[id] * add[id << 1]; 64 lazy[id << 1 | 1] += lazy[id]; 65 sum[id << 1 | 1] += lazy[id] * add[id << 1 | 1]; 66 lazy[id] = 0; 67 } 68 int mid = (l + r) >> 1; 69 if (ql <= mid) work(id << 1, l, mid, ql, qr, res); 70 if (qr > mid) work(id << 1 | 1, mid + 1, r, ql, qr, res); 71 sum[id] = sum[id << 1] + sum[id << 1 | 1]; 72 } 73 } 74 75 ll work(int u, int v) 76 { 77 ll res = 0; 78 while (top[u] != top[v]) 79 { 80 if (deep[top[u]] < deep[top[v]]) swap(u, v); 81 SEG::work(1, 1, n, p[top[u]], p[u], res); 82 u = fa[top[u]]; 83 } 84 if (deep[u] > deep[v]) swap(u, v); 85 SEG::work(1, 1, n, p[u], p[v], res); 86 return res; 87 } 88 89 void init() 90 { 91 for (int i = 1; i <= n; ++i) G[i].clear(), son[i] = 0; 92 cnt = 0; 93 } 94 95 int main() 96 { 97 scanf("%d", &t); 98 while (t--) 99 { 100 scanf("%d", &n); init(); 101 for (int i = 1; i <= n; ++i) scanf("%d", w + i); 102 for (int i = 1; i <= n; ++i) 103 { 104 scanf("%d", fa + i); 105 G[fa[i]].push_back(i); 106 } dis[1] = w[1]; DFS(1); getpos(1, 1); 107 SEG::build(1, 1, n); 108 for (int i = 2; i <= n; ++i) printf("%lld%c", work(1, i), " \n"[i == n]); 109 } 110 return 0; 111 }