• CCPC2018-湖南全国邀请赛 Solution


    A - Easy $h$-index

    后缀扫一下

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define N 200010
     6 
     7 int n;
     8 ll arr[N];
     9 
    10 inline int work()
    11 {
    12     ll sum = 0;
    13     for (int i = n; i >= 0; --i)
    14     {
    15         sum += arr[i];
    16         if (sum >= i) return i;
    17     }    
    18     return 0;
    19 }
    20 
    21 int main()
    22 {
    23     while (scanf("%d", &n) != EOF)
    24     {
    25         for (int i = 0; i <= n; ++i) scanf("%lld", arr + i);
    26         printf("%d
    ", work());
    27     }
    28     return 0;
    29 }
    View Code

    B - Higher $h$-index

    题意:有n个小时的工作量,在一篇paper上工作x小时,就能得到$a cdot x$ 次引用,每增加一篇paper ,前面的paper 引用次数就+1 ,求h-index

    思路:给每一篇paper一个小时,那么arr[]相当于 011111111111  一共有 a + n - 1 个1

    然后要满足不等式$a + n - 1 - h + 1 >= h$ 

    化简后便是 $h <= frac{a + n}{2}$

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 
     6 ll a, b;
     7 
     8 int main()
     9 {
    10     while (scanf("%lld%lld", &a, &b) != EOF)
    11     {
    12         printf("%lld
    ", (a + b) / 2);
    13     }
    14     return 0;
    15 }
    View Code

    C - Just $h$-index

    题意:给n个paper,给出每个paper的引用次数,每次询问给出 l, r 求只有这个区间内的paper有效,有h-index

    思路:主席树维护,二分h 复杂度$O(n * log(n)^2)$

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 
      4 #define N 100010
      5 #define M N * 30
      6 #define ll long long
      7 
      8 int n, q, u, v;
      9 int arr[N];
     10 int T[N], L[M], R[M], C[M], tot;
     11 
     12 inline int build(int l, int r)
     13 {
     14     int root = tot++;
     15     C[root] = 0;
     16     if (l < r)
     17     {
     18         int mid = (l + r) >> 1;
     19         L[root] = build(l, mid);
     20         R[root] = build(mid + 1, r);
     21     }
     22     return root;
     23 }
     24 
     25 inline int update(int root, int pos)
     26 {
     27     int newroot = tot++, tmp = newroot;
     28     C[newroot] = C[root] + 1;
     29     int l = 1, r = n;
     30     while (l < r)
     31     {
     32         int mid = (l + r) >> 1;
     33         if (pos <= mid)
     34         {
     35             L[newroot] = tot++, R[newroot] = R[root];
     36             newroot = L[newroot], root = L[root];
     37             r = mid;
     38         }
     39         else
     40         {
     41             L[newroot] = L[root], R[newroot] = tot++;
     42             newroot = R[newroot], root = R[root];
     43             l = mid + 1;
     44         }
     45         C[newroot] = C[root] + 1;
     46     }
     47     return tmp;
     48 }
     49 
     50 inline int query(int left_root, int right_root, int pos)
     51 {
     52     int res = 0;
     53     int l = 1, r = n;
     54     while (l < r)
     55     {
     56         int mid = (l + r) >> 1;
     57         if (pos <= mid)
     58         {
     59             left_root = L[left_root];
     60             right_root = L[right_root];
     61             r = mid;
     62         }
     63         else
     64         {
     65             res += C[L[left_root]] - C[L[right_root]];
     66             left_root = R[left_root];
     67             right_root = R[right_root];
     68             l = mid + 1;
     69         }
     70     } 
     71     return res;
     72 }
     73 
     74 
     75 int main()
     76 {
     77     while (scanf("%d%d", &n, &q) != EOF)
     78     {
     79         for (int i = 1; i <= n; ++i) scanf("%d", arr + i);
     80         tot = 0; T[n + 1] = build(1, n);
     81         for (int i = n; i >= 1; --i) T[i] = update(T[i + 1], arr[i]);
     82         for (int i = 1, u, v; i <= q; ++i)
     83         {
     84             scanf("%d%d", &u, &v);
     85             int l = 1, r = v - u + 1, ans; 
     86             while (r - l >= 0)
     87             {
     88                 int mid = (l + r) >> 1; 
     89                 int tot = v - u + 1 - query(T[u], T[v + 1], mid);
     90                 if (tot >= mid)
     91                 {
     92                     ans = mid;
     93                     l = mid + 1;
     94                 }
     95                 else
     96                     r = mid - 1;
     97             }
     98             printf("%d
    ", ans);
     99         }
    100     
    101     }
    102     return 0;
    103 }
    View Code

    D - Circular Coloring

    留坑。

    E - From Tree to Graph

    题意:给出一棵树 定义$f_i[u] = 去掉点u后的联通分量个数$ 定义$z_i = f_i(0) oplus f_i(1)  oplus ... oplus f_i(n - 1)$

     $x_i = (a cdot x_{i - 1} + b cdot y_{y - 1} + z_{i - 1}) pmod n$

    $y_i = (b cdot x_{i - 1} + a cdot y_{i - 1} + z_{i - 1}) pmod n$

    求 $x_m, y_m$

    思路:先求出$z_0$ 刚开始的联通分量个数就是每个点的出入度,然后根据异或性质,异或两次相当于消除,每次假如一条边必定形成一个环,然后并查集缩点

      1 #include <bits/stdc++.h>
      2 using namespace std; 
      3 
      4 #define N 10010
      5 
      6 struct Edge
      7 {
      8     int to, nx;
      9     inline Edge() {}
     10     inline Edge(int to, int nx) : to(to), nx(nx) {}
     11 }edge[N << 1];
     12 
     13 int n, m, a, b, x, y, ans;
     14 int head[N], pos, sz[N];
     15 int rmq[N << 1], F[N << 1], P[N], fa[N], deep[N], cnt; 
     16 int pre[N];
     17 
     18 struct ST
     19 {
     20     int mm[N << 1];
     21     int dp[N << 1][20];
     22     inline void init(int n)
     23     {
     24         mm[0] = -1;
     25         for (int i = 1; i <= n; ++i)
     26         {
     27             mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
     28             dp[i][0] = i;
     29         }
     30         for (int j = 1; j <= mm[n]; ++j)
     31         {
     32             for (int i = 1; i + (1 << j) - 1 <= n; ++i)
     33             {
     34                 dp[i][j] = rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1];
     35             }
     36         }
     37     }
     38     inline int query(int a, int b)
     39     {
     40         if (a > b) swap(a, b);
     41         int k = mm[b - a + 1];
     42         return rmq[dp[a][k]] <= rmq[dp[b - (1 << k) + 1][k]] ? dp[a][k] : dp[b - (1 << k) + 1][k];
     43     }
     44 }st;
     45 
     46 inline void Init()
     47 {
     48     memset(head, -1, sizeof head);
     49     memset(sz, 0, sizeof sz); 
     50     pos = 0; cnt = 0; ans = 0;
     51     for (int i = 1; i <= n; ++i) pre[i] = i;
     52 }
     53 
     54 inline void addedge(int u, int v)
     55 {
     56     edge[++pos] = Edge(v, head[u]); head[u] = pos;
     57 }
     58 
     59 inline void DFS(int u)
     60 {
     61     F[++cnt] = u; 
     62     rmq[cnt] = deep[u];
     63     P[u] = cnt;
     64     for (int it = head[u]; ~it; it = edge[it].nx) 
     65     {
     66         int v = edge[it].to;
     67         if (v == fa[u]) continue;
     68         fa[v] = u; deep[v] = deep[u] + 1;
     69         DFS(v); 
     70         F[++cnt] = u;
     71         rmq[cnt] = deep[u];
     72     }
     73 }
     74 
     75 inline void init_lca(int root, int node_num)
     76 {
     77     fa[root] = 0; deep[0] = 0;  
     78     DFS(root);
     79     st.init(2 * node_num - 1); 
     80 }
     81 
     82 inline int query_lca(int u, int v)
     83 {
     84     return F[st.query(P[u], P[v])];
     85 }
     86 
     87 inline int find(int x)
     88 {
     89     if (pre[x] != x)
     90         pre[x] = find(pre[x]);
     91     return pre[x]; 
     92 }
     93 
     94 inline void join(int x, int y)
     95 {
     96     x = find(x); 
     97     if (deep[fa[x]] <= deep[y] || fa[x] == 0) return;
     98     ans ^= sz[fa[x]] ^ (sz[fa[x]] - 1); 
     99     --sz[fa[x]]; 
    100     pre[x] = fa[x];
    101     join(fa[x], y); 
    102 }
    103 
    104 inline void Run() 
    105 {
    106     while (scanf("%d%d%d%d%d%d", &n, &m, &a, &b, &x, &y) != EOF) 
    107     {
    108         Init();
    109         for (int i = 1, u, v; i < n; ++i)
    110         {
    111             scanf("%d%d", &u, &v); ++u, ++v; ++sz[u], ++sz[v];
    112             addedge(u, v); addedge(v, u); 
    113         }
    114         init_lca(1, n);
    115         for (int i = 1; i <= n; ++i) ans ^= sz[i];  
    116         for (int i = 1; i <= m; ++i)
    117         {
    118             int nx = (a * x + b * y + ans) % n;
    119             int ny = (b * x + a * y + ans) % n;
    120             x = nx, y = ny;  
    121             join(x + 1, query_lca(x + 1, y + 1));
    122         }
    123         printf("%d %d
    ", x, y); 
    124     }
    125 }
    126 
    127 int main()
    128 {
    129     #ifdef LOCAL 
    130         freopen("Test.in", "r", stdin);
    131     #endif 
    132 
    133     Run();
    134     return 0;
    135 }
    View Code

    F - Sorting

    公式化简,结构体排序

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define N 1010
     6 
     7 struct node
     8 {
     9     int id;
    10     ll a, b, c, sum;
    11     inline void scan(int _id)
    12     {
    13         scanf("%lld%lld%lld", &a, &b, &c);
    14         sum = a + b;
    15         id = _id;
    16     }    
    17     inline bool operator < (const node &r) const
    18     {
    19         ll t1 = sum * r.c, t2 = r.sum * c;
    20         return t1 < t2 || t1 == t2 && id < r.id;
    21     }
    22 }arr[N];
    23 
    24 int n;
    25 
    26 int main()
    27 {
    28     while (scanf("%d", &n) != EOF)
    29     {
    30         for (int i = 1; i <= n; ++i) arr[i].scan(i);
    31         sort(arr + 1, arr + 1 + n);
    32         for (int i = 1; i <= n; ++i) printf("%d%c", arr[i].id," 
    "[i == n]);
    33     }
    34     return 0;
    35 }
    View Code

    G - String Transformation

    题意:给出一个串a和串b,每次可以在串a中添加 {"aa", "bb", "abab"} 问能否将串a变成串b

    思路:显然 两个串的c的数量不同是不可以变的 然后以c为间隔,分成若干个间隔,每个间隔里面的a,b差值不为偶数也是没法变

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 const int maxn = 1e4 + 10;
     6 
     7 int a1[maxn], b1[maxn], c1[maxn];
     8 int a2[maxn], b2[maxn], c2[maxn];
     9 char str1[maxn], str2[maxn];
    10 
    11 int main()
    12 {
    13     while(~scanf("%s", str1 + 1))
    14     {
    15         scanf("%s", str2 + 1);
    16         int n = strlen(str1 + 1);
    17         int m = strlen(str2 + 1);
    18         int cnt1 = 0;
    19         for(int i = 1; i <= n; ++i)
    20         {
    21             cnt1 += (str1[i] == 'c');
    22         }
    23         int cnt2 = 0;
    24         for(int i = 1; i <= m; ++i)
    25         {
    26             cnt2 += (str2[i] == 'c');
    27         }
    28         if(cnt1 != cnt2)
    29         {
    30             puts("No");
    31             continue;
    32         }
    33         c1[0] = c2[0] = 0;
    34         cnt1 = cnt2 = 1;
    35         for(int i = 1; i <= n; ++i)
    36         {
    37             a1[i] = a1[i - 1] + (str1[i] == 'a');
    38             b1[i] = b1[i - 1] + (str1[i] == 'b');
    39             if(str1[i] == 'c')
    40             {
    41                 c1[cnt1++] = i;
    42             }
    43         }
    44         c1[cnt1++] = n;
    45         for(int i = 1; i <= m; ++i)
    46         {
    47             a2[i] = a2[i - 1] + (str2[i] == 'a');
    48             b2[i] = b2[i - 1] + (str2[i] == 'b');
    49             if(str2[i] == 'c')
    50             {
    51                 c2[cnt2++] = i;
    52             }
    53         }
    54         c2[cnt2++] = m;
    55         bool flag = true;
    56         for(int i = 1; i < cnt1; ++i)
    57         {
    58             if((a1[c1[i]] - a1[c1[i - 1]]) % 2 != (a2[c2[i]] - a2[c2[i - 1]]) % 2 || (b1[c1[i]] - b1[c1[i - 1]]) % 2 != (b2[c2[i]] - b2[c2[i - 1]]) % 2)
    59             {
    60                 flag = false;
    61             }
    62         }
    63         puts(flag ? "Yes" : "No");
    64     }
    65     return 0;
    66 }
    View Code

    H - Infinity

    留坑。

    I - Longest Increasing Subsequence

    题意:给出n个数,有若干个数是0,定义$F[i]$ 为将所有0变成i后的最长上升子序列长度  求$sum_{i = 1}^{i = n} i cdot F[i]$

    思路:预处理出$dp[], dp2[]$ $dp[i]$表示以$i$结尾的最长上升子序列长度,$dp2[i]$ 表示以$i$开头的最长上升子序列长度

    我们考虑$F[i] = (Minlen, Minlen + 1)$

    我们先求出 去掉所有0的最长上升子序列长度  然后考虑 $dp[i] + dp2[j] = m$  并且 $[i, j]$ 中有0存在 并且 $arr[i] < arr[j]$  那么

    $x in [arr[i] + 1, arr[j] - 1]$ 的时候 $F[x] = len + 1$

     1 #include <bits/stdc++.h>
     2 using namespace std; 
     3 
     4 #define N 100010 
     5 #define INF 0x3f3f3f3f
     6 #define ll long long
     7 
     8 int n, m;
     9 int dp[N], dp2[N], arr[N], brr[N], a[N]; 
    10 
    11 inline void LIS()
    12 {
    13     int len = 0; brr[1] = 0; 
    14     for (int i = 1; i <= n; ++i)
    15     { 
    16         if (arr[i] == 0) continue;  
    17         int pos = lower_bound(brr + 1, brr + 1 + len, arr[i]) - brr;
    18         if (pos > len) ++len;  
    19         dp[i] = pos; brr[pos] = arr[i];
    20     }
    21     m = len; len = 0; brr[1] = 0;  
    22     for (int i = n; i >= 1; --i)
    23     {
    24         if (arr[i] == 0) continue; 
    25         int pos = lower_bound(brr + 1, brr + 1 + len, -arr[i]) - brr;
    26         if (pos > len) ++len; 
    27         dp2[i] = pos; brr[pos] = -arr[i]; 
    28     } 
    29 } 
    30 
    31 
    32 inline void Run() 
    33 {
    34     while (scanf("%d", &n) != EOF)
    35     {
    36         for (int i = 1; i <= n; ++i) scanf("%d", arr + i); LIS(); 
    37         memset(a, 0, sizeof a); memset(brr, 0x3f, sizeof brr); brr[0] = 0;  
    38         int now = 0;
    39         for (int i = 1; i <= n; ++i) 
    40         {
    41             if (arr[i] == 0) 
    42             {
    43                 for (int j = now + 1; j < i; ++j) 
    44                 {
    45                     brr[dp[j]] = min(brr[dp[j]], arr[j]);   
    46                 }
    47                 now = i; 
    48                 continue;
    49             }  
    50             if (now && brr[m - dp2[i]] < arr[i]) 
    51             { 
    52                 a[brr[m - dp2[i]] + 1]++;
    53                 a[arr[i]]--; 
    54             }
    55         } 
    56         if (now && brr[m] != INF)   
    57         { 
    58             a[brr[m] + 1]++; 
    59         }
    60         for (int i = 1; i <= n; ++i) a[i] += a[i - 1];
    61         ll ans = 0; 
    62         for (int i = 1; i <= n; ++i) ans += (ll)i * (m + (a[i] ? 1 : 0));  
    63         printf("%lld
    ", ans);  
    64     }
    65 }
    66 
    67 int main()
    68 {
    69     #ifdef LOCAL 
    70         freopen("Test.in", "r", stdin);
    71     #endif 
    72 
    73     Run();
    74     return 0;
    75 }
    View Code

    J - Vertex Cover

    题意:有n个点,标号为0 - n - 1每个点的权值就是$2_i$ i 为标号,alice 任意选择一种边集,bob选择一种权值和最小的边际覆盖它的边集,覆盖的定义为alice中每条边中至少有一个点在bob的边集当中,并且权值和为k

    思路:因为权值为2的幂,所以如果bob存在一种选择满足要求,只有一种,那就是k

    那么相当于固定bob的选择,找alice有多少种选择被bob覆盖

    从左往右扫,如果遇到一个点是0,那么这个点可以和前面的1连一条边

    如果遇到一个点是1,那么这个点至少要在前面选择一个没有选的点相连,其他点可连可不连,所有可能性相乘

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 #define MOD 1000000007
     6 #define N 100010
     7 
     8 ll Bit[N];
     9 
    10 inline void Init()
    11 {
    12     Bit[0] = 1;
    13     for (int i = 1; i <= 100000; ++i) Bit[i] = (Bit[i - 1] << 1) % MOD;
    14 }
    15 
    16 int n;
    17 char s[N];
    18 
    19 int main()
    20 {
    21     Init();
    22     while (scanf("%d", &n) != EOF)
    23     {
    24         scanf("%s", s);
    25         int len = strlen(s);
    26         ll ans = 1; int k = n - len, cnt = 0;
    27         for (int i = 0; i < len; ++i, ++k)
    28         {
    29             if (s[i] == '1')
    30             {
    31                 ans = (ans *(Bit[k] - Bit[cnt] + MOD) % MOD) % MOD; 
    32                 ++cnt;
    33             }
    34             else
    35             {
    36                 ans = (ans * Bit[cnt]) % MOD;
    37             }
    38         }
    39         printf("%lld
    ", ans);
    40     }
    41     return 0;
    42 }
    View Code

    K - 2018

    题意:给出a, b, c, d, 找出有多少个二元组(x, y) 满足 $ x cdot y equiv 0 pmod 2018$

    思路:显然,2018只能拆成1009 * 2

    那么只要在(a, b) 中有多少1009 倍数 2008倍数  然后计算 注意去重

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define ll long long
     5 
     6 ll x, y;
     7 ll a, b, c, d;
     8 
     9 int main()
    10 {
    11     while (scanf("%lld%lld%lld%lld", &a, &b, &c, &d) != EOF)
    12     {
    13         ll ans = 0;
    14         
    15         x = (b / 2018) - ((a - 1) / 2018);
    16         y = (d - c + 1);
    17         ans += x * y;
    18 
    19         x = (d / 2018) - ((c - 1) / 2018);
    20         y = (b - a + 1);
    21         ans += x * y;
    22 
    23         x = (b / 2018) - ((a - 1) / 2018);
    24         y = (d / 2018) - ((c - 1) / 2018);
    25         ans -= x * y;
    26 
    27         x = (((b / 1009) + 1) / 2) - ((((a - 1) / 1009) + 1) / 2);
    28         y = ((d / 2) - ((c - 1) / 2)) - ((d / 2018) - ((c - 1) / 2018));
    29         ans += x * y;
    30 
    31         x = (((d / 1009) + 1) / 2) - ((((c - 1) / 1009) + 1) / 2);
    32         y = ((b / 2) - ((a - 1) / 2)) - ((b / 2018) - ((a - 1) / 2018));
    33         ans += x * y;
    34 
    35         printf("%lld
    ", ans);
    36     }
    37     return 0;
    38 }
    View Code
  • 相关阅读:
    剖析VC++函数调用约定转
    C++的坑真的多吗?转
    An Introduction to LockFree Programming转
    __cdecl __stdcall区别转
    学习PHP感谢帅哥分享O(∩_∩)O~
    28个Unix/Linux的命令行神器转
    C++ 对象的内存布局(上)转
    一个fork的面试题转
    20本最好的Linux免费书籍转
    谁说外国人都很文明
  • 原文地址:https://www.cnblogs.com/Dup4/p/9593797.html
Copyright © 2020-2023  润新知