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 }
View Code

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
", 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 }
View Code

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
",ans);
49     }
50     return 0;
51 }
View Code

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
",ans);
66     }
67     return 0;
68 }
View Code

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
",ans);
46     }
47     return 0;
48 }
View Code

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
", 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 }
View Code

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
%d
", i);
 74             return;
 75         }
 76         if (GCD(num[i], num[fa[i]]) == 1)
 77             id = i;
 78     }
 79     printf("2
%d %d
", 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 }
View Code

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 }
View Code

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
", ans);
36     }    
37     return 0;
38 }
View Code

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
",ans);
166         }
167     }
168     return 0;
169 }
View Code

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), " 
"[i == n]);
109     }
110     return 0;
111 }
View Code
原文地址:https://www.cnblogs.com/Dup4/p/9506874.html