• 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
  • 相关阅读:
    Spark SQL (一)
    hdu 3729 最大匹配
    1350 Taxi Cab Scheme DAG最小路径覆盖
    hdu 2768 Cat vs. Dog 最大独立集 巧妙的建图
    hdu 2444 The Accomodation of Students 判断是否构成二分图 + 最大匹配
    hdu1507 最大匹配
    二分图的最大独立集 最大匹配解题 Hopcroft-Karp算法
    有向无环图的最小路径覆盖 二分图模型解题
    二分图最小点覆盖
    hdu3488 / hdu3435 / hdu1853 最小费用最大流 圈 拆点
  • 原文地址:https://www.cnblogs.com/Dup4/p/9506874.html
Copyright © 2020-2023  润新知