zoukankan      html  css  js  c++  java
  • 2020牛客多校训练赛第二场合集


    A、All with Pairs

    题意:

    给出$n$个串,定义$f(s,t)$是$s$的前缀和$t$的后缀的最长相同的长度,求$sum limits _{1leq ileq jleq n}f(s_i,s_j)$。

    题解:

    看到前缀长度等于后缀长度,想到什么?对,就是$kmp$的$next$数组,但是这里只要最长的,所以对于比较短的相同前缀,就需要去重。但是我们暂时还不知道怎么去重,然后我们需要快速地找这些前缀后缀,使用哈希就很棒,为了防卡,使用双哈希就很棒。然后先把所有的字符串的后缀插到哈希表,然后对每一个串进行前缀查询,找到和这个串的前缀相同的哈希值的后缀的数量,然后考虑去重,比如,需要去重的是$"aba"$中的$"a"$子串,因为字符串枚举前缀的时候,是从大到小枚举,所以就考虑碰到一个前缀$j$,就减去这个串已经统计的部分。

    AC代码:

     1 #include <bits/stdc++.h>
     2 #include <unordered_map>
     3 using namespace std;
     4 const int N = 1e5 + 5;
     5 const int M = 1e6 + 5;
     6 typedef long long ll;
     7 string str[N];
     8 int nxt[M];
     9 ll hmod[2] = {998244353, 1000000007}, base[2] = {131, 37};
    10 ll hs[2][M], f[2][M];
    11 ll vis[M];
    12 struct p_hash
    13 {
    14     template <class T1, class T2>
    15     size_t operator()(const pair<T1, T2> &p) const
    16     {
    17         return hash<T1>{}(p.first) ^ hash<T2>{}(p.second);
    18     }
    19 };
    20 unordered_map<pair<ll, ll>, ll, p_hash> mp;
    21 void init()
    22 {
    23     f[0][0] = f[1][0] = 1;
    24     for (int i = 0; i <= 1; ++i)
    25         for (int j = 1; j < M; ++j)
    26             f[i][j] = f[i][j - 1] * base[i] % hmod[i];
    27 }
    28 ll get_hash(int l, int r, int j)
    29 {
    30     return (hs[j][r] - hs[j][l - 1] * f[j][r - l + 1] % hmod[j] + hmod[j]) % hmod[j];
    31 }
    32 void get_next(const string &str)
    33 {
    34     nxt[0] = -1;
    35     int j = -1, k = 0;
    36     while (k < str.size())
    37     {
    38         if (j == -1 || str[j] == str[k])
    39             nxt[++k] = ++j;
    40         else
    41             j = nxt[j];
    42     }
    43 }
    44 const ll mod = 998244353;
    45 int main()
    46 {
    47     init();
    48     ios::sync_with_stdio(0);
    49     cin.tie(0);
    50     int n;
    51     cin >> n;
    52     for (int i = 1; i <= n; ++i)
    53         cin >> str[i];
    54     for (int i = 1; i <= n; ++i)
    55     {
    56         pair<ll, ll> tmp;
    57         int len = str[i].size();
    58         for (int j = 0; j <= 1; ++j)
    59             for (int k = 0; k < len; ++k)
    60                 hs[j][k + 1] = (hs[j][k] * base[j] % hmod[j] + str[i][k] - 'a' + 1) % hmod[j];
    61         for (int j = 1; j <= len; ++j)
    62         {
    63             tmp.first = get_hash(j, len, 0);
    64             tmp.second = get_hash(j, len, 1);
    65             ++mp[tmp];
    66         }
    67     }
    68     ll ans = 0;
    69     for (int i = 1; i <= n; ++i)
    70     {
    71         get_next(str[i]);
    72         int len = str[i].size();
    73         memset(vis, 0, sizeof(vis[0]) * (str[i].size() + 1));
    74         for (int j = 0; j <= 1; ++j)
    75             for (int k = 0; k < len; ++k)
    76                 hs[j][k + 1] = (hs[j][k] * base[j] % hmod[j] + str[i][k] - 'a' + 1) % hmod[j];
    77         pair<ll, ll> tmp;
    78         for (int j = len; j; --j)
    79         {
    80             tmp.first = get_hash(1, j, 0);
    81             tmp.second = get_hash(1, j, 1);
    82             ans = (ans + (mp[tmp] - vis[j] + mod) % mod * j % mod * j % mod) % mod;
    83             int tnxt = nxt[j];
    84             vis[tnxt] = (vis[tnxt] + mp[tmp]) % mod;
    85         }
    86     }
    87     cout << ans << endl;
    88     return 0;
    89 }
    View Code

    B、Boundary

    题意:

    给出一些点,问最多有几个点和$(0,0)$共一个圆?$nleq 2e3$。

    题解:

    显然不能$O(n^3)$暴力枚举点对。所以我们就只能枚举一个点,然后因为$(0,0)$也在圆上,然后对于第三个点,它们在一个圆上,圆周角就要一样。然后直接根据向量规则求夹角的余弦值就行,但是这个题余弦值的精度差很小,所以需要对这些数乘$1e14$变成整数再存进$map$里面。如果直接算出圆心的位置,对精度的要求就会小一些。

    AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 2e3 + 5;
     4 unordered_map<long long, int> mp;
     5 pair<int, int> p[N];
     6 pair<int, int> operator-(const pair<int, int> &a, const pair<int, int> &b)
     7 {
     8     return make_pair(a.first - b.first, a.second - b.second);
     9 }
    10 double dist(const pair<int, int> &vec)
    11 {
    12     return sqrt(vec.first * vec.first + vec.second * vec.second);
    13 }
    14 double dotcos(const pair<int, int> &a, const pair<int, int> &b)
    15 {
    16     return (a.first * b.first + a.second * b.second) / (dist(a) * dist(b));
    17 }
    18 int cross(const pair<int, int> &a, const pair<int, int> &b)
    19 {
    20     return a.first * b.second - b.first * a.second;
    21 }
    22 int main()
    23 {
    24     int n;
    25     scanf("%d", &n);
    26     for (int i = 1; i <= n; ++i)
    27         scanf("%d%d", &p[i].first, &p[i].second);
    28     int maxn = 0;
    29     for (int i = 1; i <= n; ++i)
    30     {
    31         mp.clear();
    32         for (int j = 1; j <= n; ++j)
    33             if (cross(p[i], p[j]) < 0)
    34                 ++mp[dotcos(p[j] - make_pair(0, 0), p[j] - p[i]) * 1e14];
    35         for (auto &j : mp)
    36             maxn = max(maxn, j.second);
    37     }
    38     printf("%d
    ", maxn + 1);
    39     return 0;
    40 }
    View Code

    C、Cover the Tree

    题意:

    给出一棵树,每次找两个点连路径,求最小需要连多少路径使得树上每条边都覆盖。

    题解:

    显然都连叶子最优。自己写的时候考虑了类似于树的重心,找到了叶节点的重心,但是实现太垃圾,一直不是$wa$就是$tle$。

    看了正解才发现真的简单。直接看代码就行,正确性:设$s$是叶子节点个数,所有前$s/2$和后$s/2$个叶子结点往上的所有边都会被覆盖,这样子不可能找到一棵树,还有未被覆盖的边。

    AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 2e5 + 5;
     4 typedef long long ll;
     5 vector<int> G[N], lef;
     6 void dfs(int u, int fa)
     7 {
     8     if (G[u].size() == 1)
     9     {
    10         lef.push_back(u);
    11         return;
    12     }
    13     for (auto i : G[u])
    14         if (i != fa)
    15             dfs(i, u);
    16 }
    17 void solve()
    18 {
    19     int n;
    20     scanf("%d", &n);
    21     for (int i = 1; i < n; ++i)
    22     {
    23         int u, v;
    24         scanf("%d%d", &u, &v);
    25         G[u].push_back(v);
    26         G[v].push_back(u);
    27     }
    28     if (n == 1)
    29     {
    30         printf("0
    ");
    31         return;
    32     }
    33     if (n == 2)
    34     {
    35         printf("1
    1 2
    ");
    36         return;
    37     }
    38     int rt = 0;
    39     for (int i = 1; i <= n; ++i)
    40         if (G[i].size() != 1)
    41         {
    42             rt = i;
    43             break;
    44         }
    45     dfs(rt, 0);
    46     printf("%d
    ", (lef.size() + 1) / 2);
    47     for (int i = 0; i < lef.size() / 2; ++i)
    48         printf("%d %d
    ", lef[i], lef[i + (lef.size() + 1) / 2]);
    49     if (lef.size() % 2)
    50         printf("%d %d
    ", lef[lef.size() / 2], rt);
    51 }
    52 int main()
    53 {
    54     int T = 1;
    55     //scanf("%d", &T);
    56     while (T--)
    57         solve();
    58     return 0;
    59 }
    View Code

    D、Duration

    题意:

    太简单了不说了。

    题解:

    随便减一下取个绝对值就行。

    AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e5 + 5;
     4 typedef long long ll;
     5 char s[10];
     6 void solve()
     7 {
     8     scanf("%s", s);
     9     int hh = s[0] * 10 + s[1];
    10     int mm = s[3] * 10 + s[4];
    11     int ss = s[6] * 10 + s[7];
    12     int tot = hh * 3600 + mm * 60 + ss;
    13     scanf("%s", s);
    14     hh = s[0] * 10 + s[1];
    15     mm = s[3] * 10 + s[4];
    16     ss = s[6] * 10 + s[7];
    17     tot = hh * 3600 + mm * 60 + ss - tot;
    18     printf("%d
    ", abs(tot));
    19 }
    20 int main()
    21 {
    22     int T = 1;
    23     //scanf("%d", &T);
    24     while (T--)
    25         solve();
    26     return 0;
    27 }
    View Code

    E、Exclusive OR

    队友补。

    F、Fake Maxpooling

    题意:

    设矩阵中$a_{ij} = lcm(i,j)$,求边长为$k$的子方阵最大值的和。$nleq 5e3,mleq 5e3$。

    题解:

    构造矩阵的时候需要利用线性筛的思路优化,虽然暴力求解卡常也能过。然后就套路题了,单调队列套单调队列。

    坑点:不能$a[i][j]=a[j][i]=lcm(i,j)$,因为不保证一定是方阵,如果非要这样子,就要求完长度是$max(n,m)$的方阵。

    AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn = 5100;
     5 int aa[maxn][maxn], mp[maxn][maxn], q[maxn];
     6 inline int gcd(int a, int b)
     7 {
     8     return !b ? a : gcd(b, a % b);
     9 }
    10 inline int lcm(int a, int b)
    11 {
    12     return a * b / gcd(a, b);
    13 }
    14 int main()
    15 {
    16     int n, m, a, b, k;
    17     scanf("%d%d%d", &n, &m, &k);
    18     a = k, b = k;
    19     for (int i = 1; i <= n; ++i)
    20         for (int j = 1; j <= m; ++j)
    21             aa[i][j] = lcm(i, j);
    22     for (int i = 1; i <= n; i++)
    23     {
    24         int l = 1, r = 0;
    25         for (int j = 1; j <= b; j++)
    26         {
    27             while (r && aa[i][q[r]] < aa[i][j])
    28                 r--;
    29             q[++r] = j;
    30         }
    31         mp[i][1] = aa[i][q[l]];
    32         for (int j = 2; j + b - 1 <= m; j++)
    33         {
    34             if (q[l] == j - 1)
    35                 l++;
    36             while (l <= r && aa[i][q[r]] < aa[i][j + b - 1])
    37                 r--;
    38             q[++r] = j + b - 1;
    39             mp[i][j] = aa[i][q[l]];
    40         }
    41     }
    42     ll ans = 0;
    43     for (int j = 1; j <= m; j++)
    44     {
    45         int l = 1, r = 0;
    46         for (int i = 1; i <= a; i++)
    47         {
    48             while (r && mp[q[r]][j] < mp[i][j])
    49                 r--;
    50             q[++r] = i;
    51         }
    52         (ans += mp[q[l]][j]);
    53         for (int i = 2; i + a - 1 <= n; i++)
    54         {
    55             if (q[l] == i - 1)
    56                 l++;
    57             while (l <= r && mp[q[r]][j] < mp[i + a - 1][j])
    58                 r--;
    59             q[++r] = i + a - 1;
    60             (ans += mp[q[l]][j]);
    61         }
    62     }
    63     printf("%lld
    ", ans);
    64     return 0;
    65 }
    View Code

    G、Greater and Greater

    题意:

    给出$a$和$b$序列,问$a$当中有多少个子数组,对应位置的数大小都大于$b$。

    题解:

    看数据范围暴力可以卡常过,所以考虑怎么卡常,$bitset$是卡常利器。所以我们就想一下怎么用$bitset$卡常,然后把$bitset$的$a_i$大于$b_j$的$i$位设成$1$,最后右移$j$位,得到合法的数的数量。然后对于每个$b$序列都这么处理,最后取与即可。实现的时候,先降序排序然后用一个$bitset$记录就行了,因为前面枚举到的数一定大于后面枚举到的。

    AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1.5e5 + 5;
     4 bitset<N> f, g;
     5 struct node
     6 {
     7     int val, id;
     8 };
     9 node a[N], b[N];
    10 int main()
    11 {
    12     int n, m;
    13     scanf("%d%d", &n, &m);
    14     for (int i = 1; i <= n; ++i)
    15         scanf("%d", &a[i].val), a[i].id = i;
    16     for (int i = 1; i <= m; ++i)
    17         scanf("%d", &b[i].val), b[i].id = i;
    18     sort(a + 1, a + n + 1, [](const node &a, const node &b) -> bool { return a.val > b.val; });
    19     sort(b + 1, b + m + 1, [](const node &a, const node &b) -> bool { return a.val > b.val; });
    20     f.set();
    21     g.reset();
    22     for (int i = 1, j = 1; i <= m; ++i)
    23     {
    24         while (j <= n && a[j].val >= b[i].val)
    25             g.set(a[j++].id);
    26         //cout << "g=" << g << endl;
    27         f &= g >> b[i].id;
    28         //cout << "f=" << f << endl;
    29     }
    30     printf("%d
    ", f.count());
    31     return 0;
    32 }
    View Code

    H、Happy Triangle

    题意:

    维护一个集合$s$,给出以下指令:

    $1$、添加一个数

    $2$、删除一个数

    $3$、给出一个数,询问里面有没有两个数和这个数组成非退化三角形。

    题解:

    操作$1$和$2$都是小意思,康康第三个怎么整。如果$x$是最大边,这样子找两个前驱就行了,如果不是,就找两个$a$,$b$,且$a$要大于等于$x$和$b$,$a-b$小于$x$。关键是维护这个有序序列的相邻差值和求区间最小值,使用权值线段树。线段树维护三个值,区间最小值,区间最大值和区间最小的差值。对于长度是$1$的区间,如果有几个数,则差值是$0$,如果只有一个数,视为无穷大,差值的维护就是,对于一段区间,最小差值来自子区间和跨区间取得(就是右区间最小减左区间最大),取最小上推。查询同理。

    AC代码:

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 const int N = 4e5 + 5;
      4 const int inf = 0x7fffffff;
      5 int mp[N];
      6 int b[N], top;
      7 struct segtree
      8 {
      9 #define ls (k << 1)
     10 #define rs ((k << 1) + 1)
     11     struct node
     12     {
     13         int l, r, maxn, minn, gap;
     14     };
     15     node tr[N << 1];
     16     inline void pushup(int k)
     17     {
     18         tr[k].maxn = max(tr[ls].maxn, tr[rs].maxn);
     19         tr[k].minn = min(tr[ls].minn, tr[rs].minn);
     20         tr[k].gap = min(tr[ls].gap, tr[rs].gap);
     21         tr[k].gap = min(tr[k].gap, tr[rs].minn - tr[ls].maxn);
     22     }
     23     void build(int l, int r, int k)
     24     {
     25         tr[k].l = l;
     26         tr[k].r = r;
     27         if (l == r)
     28         {
     29             tr[k].maxn = 0;
     30             tr[k].minn = inf;
     31             tr[k].gap = inf;
     32             return;
     33         }
     34         int m = (l + r) >> 1;
     35         build(l, m, ls);
     36         build(m + 1, r, rs);
     37         pushup(k);
     38     }
     39     void update(int k, int pos, int val)
     40     {
     41         if (tr[k].l == tr[k].r)
     42         {
     43             mp[pos] += val;
     44             tr[k].gap = inf;
     45             if (mp[pos] == 0)
     46             {
     47                 tr[k].maxn = 0;
     48                 tr[k].minn = inf;
     49             }
     50             else
     51             {
     52                 tr[k].maxn = tr[k].minn = b[pos];
     53                 if (mp[pos] >= 2)
     54                     tr[k].gap = 0;
     55             }
     56             return;
     57         }
     58         int m = (tr[k].l + tr[k].r) >> 1;
     59         if (pos <= m)
     60             update(ls, pos, val);
     61         else
     62             update(rs, pos, val);
     63         pushup(k);
     64     }
     65     int querym(int l, int r, int k, int op)
     66     {
     67         if (l > r)
     68             return op ? 0 : inf;
     69         if (l <= tr[k].l && tr[k].r <= r)
     70             return op ? tr[k].maxn : tr[k].minn;
     71         int m = (tr[k].l + tr[k].r) >> 1;
     72         int ans1 = 0, ans2 = inf, tmp;
     73         if (l <= m)
     74         {
     75             tmp = querym(l, r, ls, op);
     76             ans1 = max(ans1, tmp);
     77             ans2 = min(ans2, tmp);
     78         }
     79         if (r > m)
     80         {
     81             tmp = querym(l, r, rs, op);
     82             ans1 = max(ans1, tmp);
     83             ans2 = min(ans2, tmp);
     84         }
     85         return op ? ans1 : ans2;
     86     }
     87     int queryg(int l, int r, int k)
     88     {
     89         if (l > r)
     90             return inf;
     91         if (l <= tr[k].l && tr[k].r <= r)
     92             return tr[k].gap;
     93         int m = (tr[k].l + tr[k].r) >> 1;
     94         int ans = inf;
     95         int f = 0;
     96         if (l <= m)
     97             ans = min(ans, queryg(l, r, ls)), ++f;
     98         if (r > m)
     99             ans = min(ans, queryg(l, r, rs)), ++f;
    100         if (f == 2)
    101             ans = min(ans, tr[rs].minn - tr[ls].maxn);
    102         return ans;
    103     }
    104 #undef ls
    105 #undef rs
    106 };
    107 pair<int, int> q[N];
    108 #define x first
    109 #define y second
    110 segtree tr;
    111 bool check(int op, int pos, int val)
    112 {
    113     if (mp[pos] >= 2)
    114         return 1;
    115     else if (mp[pos] == 1)
    116     {
    117         int maxn = tr.querym(1, pos - 1, 1, 1);
    118         if (maxn != 0)
    119             return 1;
    120         int ming = tr.queryg(pos, top, 1);
    121         if (ming < val)
    122             return 1;
    123         return 0;
    124     }
    125     int maxn = tr.querym(1, pos, 1, 1);
    126     int minn = tr.querym(pos, top, 1, 0);
    127     if (maxn != 0 && minn != inf && maxn + val > minn)
    128         return 1;
    129     int mpos = lower_bound(b + 1, b + top + 1, maxn) - b;
    130     if (maxn)
    131     {
    132         tr.update(1, mpos, -1);
    133         int smaxn = tr.querym(1, pos, 1, 1);
    134         tr.update(1, mpos, 1);
    135         if (smaxn && smaxn + maxn > val)
    136             return 1;
    137     }
    138     int ming = tr.queryg(pos, top, 1);
    139     if (ming < val)
    140         return 1;
    141     return 0;
    142 }
    143 int main()
    144 {
    145     int n;
    146     scanf("%d", &n);
    147     for (int i = 1; i <= n; ++i)
    148     {
    149         scanf("%d%d", &q[i].x, &q[i].y);
    150         b[++top] = q[i].y;
    151     }
    152     sort(b + 1, b + top + 1);
    153     top = unique(b + 1, b + top + 1) - b - 1;
    154     for (int i = 1; i <= n; ++i)
    155         q[i].y = lower_bound(b + 1, b + top + 1, q[i].y) - b;
    156     tr.build(1, top, 1);
    157     for (int i = 1; i <= n; ++i)
    158     {
    159         if (q[i].x == 1)
    160             tr.update(1, q[i].y, 1);
    161         else if (q[i].x == 2)
    162             tr.update(1, q[i].y, -1);
    163         else
    164             printf("%s", check(q[i].x, q[i].y, b[q[i].y]) ? "Yes
    " : "No
    ");
    165     }
    166     return 0;
    167 }
    View Code

    I、Interval

    队友补。

    J、Just Shuffle

    队友补

    AC代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e5 + 5;
     4 int a[N], b[N];
     5 vector<int> v;
     6 bool vis[N];
     7 int main()
     8 {
     9     int n, m;
    10     scanf("%d%d", &n, &m);
    11     for (int i = 1; i <= n; ++i)
    12         scanf("%d", &a[i]);
    13     for (int i = 1; i <= n; ++i)
    14         if (!vis[i])
    15         {
    16             v.clear();
    17             int x = a[i];
    18             while (!vis[x])
    19             {
    20                 vis[x] = 1;
    21                 v.push_back(x);
    22                 x = a[x];
    23             }
    24             int inv, r = v.size();
    25             for (int i = 0; i < r; ++i)
    26                 if (1ll * m * i % r == 1)
    27                     inv = i;
    28             for (int i = 0; i < r; ++i)
    29                 b[v[i]] = v[(i + inv) % r];
    30         }
    31     for (int i = 1; i <= n; ++i)
    32         printf("%d%c", b[i], " 
    "[i == n]);
    33     return 0;
    34 }
    View Code

    K、Keyboard Free

    队友补。

  • 相关阅读:
    多线程(5)async&await
    多线程(4)Task
    多线程(3)ThreadPool
    多线程(2)Thread
    多线程(1)认识多线程
    泛型
    反射(4)反射性能问题:直接调用vs反射调用
    反射(3)反射应用:一个插件项目
    反射(2)使用反射
    反射(1)认识反射
  • 原文地址:https://www.cnblogs.com/Aya-Uchida/p/13324962.html
Copyright © 2011-2022 走看看