zoukankan      html  css  js  c++  java
  • 牛客小白月赛11题解简录

    题目传送

    官方题解

    A.官方说是随机数据所以暴力线段树……

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 5;
    int N, L;
    
    class SegmentTree {
    public:
        #define ls(p) p << 1
        #define rs(p) p << 1 | 1
        struct Node {
            int l, r, minn, tag;
        }t[maxn << 2];
        
        void Push_up(int p) {
            t[p].minn = min(t[ls(p)].minn, t[rs(p)].minn);
        }
        
        void Push_down(int p) {
            if (t[p].tag) {
                t[ls(p)].minn += t[p].tag;
                t[rs(p)].minn += t[p].tag;
                t[ls(p)].tag += t[p].tag;
                t[rs(p)].tag += t[p].tag;
                t[p].tag = 0;
            }
        }
        
        void Build(int l, int r, int p) {
            t[p].l = l, t[p].r = r;
            if (l == r) {
                t[p].minn = t[p].tag = 0;
                return;
            }
            int mid = (l + r) >> 1;
            Build(l, mid, ls(p));
            Build(mid + 1, r, rs(p));
        }
        
        void Modify(int l, int r, int p, int k) {
            if (l <= t[p].l && t[p].r <= r) {
                t[p].minn += k;
                t[p].tag += k;
                return;
            }
            Push_down(p);
            int mid = (t[p].l + t[p].r) >> 1;
            if (l <= mid)    Modify(l, r, ls(p), k);
            if (mid < r)    Modify(l, r, rs(p), k);
            Push_up(p);
        }
        
        int Query(int l, int r, int p) {
            if (l <= t[p].l && t[p].r <= r && t[p].minn > 0)    return t[p].r - t[p].l + 1;
            if (t[p].l == t[p].r)    return t[p].minn > 0;
            Push_down(p);
            int mid = (t[p].l + t[p].r) >> 1;
            if (l > mid)    return Query(l, r, rs(p));
            if (r <= mid)    return Query(l, r, ls(p));
            return Query(l, r, ls(p)) + Query(l, r, rs(p));
        }
    }T;
    
    map<pair<int, int>, int> mp;
    
    int main() {
        scanf("%d %d", &N, &L);
        T.Build(1, L + 1, 1);
        for (int i = 0; i < N; i++) {
            int op, x, y;
            scanf("%d %d %d", &op, &x, &y);
            x++, y++;
            if (x > y)    swap(x, y);
            if (op == 1 && !mp[make_pair(x, y)]) {
                T.Modify(x, y, 1, 1);
                mp[make_pair(x, y)]++;
            } else if (op == 2 && mp[make_pair(x, y)]) {
                T.Modify(x, y, 1, -1);
                mp[make_pair(x, y)]--;
            } else if (op == 3) {
                cout << T.Query(1, L + 1, 1) << endl;
            }
        }
    
        return 0;
    }
    A

    B.方法是拆点,把每个点都拆成k+1个,表示已经使用过k(k属于0~K)个戒严到达此点。连边很关键。

    1.连有向边,如果u本身是戒严的,则枚举u + k * n,连向v + (k + 1) * n,意义是到u用了k个,则想到达v就得用k + 1个了。否则u + k * n连向v + k * n即可,意义为不增加使用次数。而且如果u是戒严的,则k == K的个点根本不用连了,不可能再前进一步。从v向u连的也是一样的。

    2.设立虚的起始点s和终点t,s只要和点1连接即可,因为s到点1(等价类)路过的戒严点不可能比0大。这也意味着其实我们连了这么多条边,大部分边其实都是与主图剥离的,也就是真正跑的时候根本没机会用到。而终点t就不同了,由于无法人为判定来“剪枝”,所以0~K都要与t相连。

    3.虽然边多了点,dijkstra会被卡常可还行?不是说命题人会卡spfa吗……

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 typedef long long ll;
     5 const ll INF = 2e18;
     6 const int N = 805, M = 4005;
     7 
     8 int n, m, k, hold[N], tot, s, t;
     9 ll dis[N * 11];
    10 bool vis[N * 11];
    11 int head[N * 11];
    12 struct Edge {
    13     int to, nxt;
    14     ll cost;
    15 }e[M * 22];
    16 
    17 void Add(int u, int v, ll cost) {
    18     e[++tot].to = v, e[tot].nxt = head[u], e[tot].cost = cost, head[u] = tot;
    19 }
    20 
    21 void Get_Graph() {
    22     for (int i = 1; i <= m; i++) {
    23         int u, v;
    24         ll cost;
    25         scanf("%d %d %lld", &u, &v, &cost);
    26         if (!hold[u]) {
    27             for (int j = 0; j <= k; ++j) {
    28                 Add(u + j * n, v + j * n, cost);
    29             }
    30         }
    31         if (!hold[v]) {
    32             for (int j = 0; j <= k; ++j) {
    33                 Add(v + j * n, u + j * n, cost);
    34             }
    35         }
    36         if (hold[u]) {
    37             for (int j = 0; j < k; ++j) {
    38                 Add(u + j * n, v + (j + 1) * n, cost);
    39             }
    40         }
    41         if (hold[v]) {
    42             for (int j = 0; j < k; ++j) {
    43                 Add(v + j * n, u + (j + 1) * n, cost);
    44             }
    45         }
    46     }
    47 
    48     s = 0, t = n * (k + 1) + 1;
    49     Add(s, 1, 0);
    50     for (int i = 0; i <= k; i++) {
    51         Add(n + i * n, t, 0);
    52     }
    53 }
    54 
    55 ll SPFA() {
    56     for (int i = s; i <= t; i++)
    57         dis[i] = INF, vis[i] = false;
    58     dis[s] = 0;
    59     vis[s] = true;
    60     
    61     queue<int> Q;
    62     Q.push(s);
    63     
    64     while (Q.size()) {
    65         int x = Q.front(); Q.pop();
    66         vis[x] = false;
    67         
    68         for (int i = head[x]; i; i = e[i].nxt) {
    69             int to = e[i].to;
    70             if (dis[to] > dis[x] + e[i].cost) {
    71                 dis[to] = dis[x] + e[i].cost;
    72                 if (!vis[to])    Q.push(to);
    73             }
    74         }
    75     }
    76     
    77     return dis[t];
    78 }
    79 
    80 int main() {
    81     scanf("%d %d %d", &n, &m, &k);
    82     for (int i = 1; i <= n; i++)
    83         scanf("%d", &hold[i]);
    84     Get_Graph();
    85     
    86     ll ans = SPFA();
    87     printf("%lld
    ", ans == INF ? -1 : ans);
    88     
    89     return 0;
    90 }
    B

     C.签到。模拟一下即可。话说我为啥要开个map强行提高复杂度……

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int n, m, T;
     5 map<int, int> row, column;
     6 
     7 int main() {
     8     scanf("%d %d %d", &n, &m, &T);
     9     vector<vector<int>> v(n, vector<int>(m, 0));
    10     
    11     for (int i = 1; i <= T; ++i) {
    12         int op, x;
    13         scanf("%d %d", &op, &x);
    14         x--;
    15         if (op == 1)    row[x] = i;
    16         else    column[x] = i;
    17     }
    18     
    19     for (int i = 0; i < n; i++)
    20         for (int j = 0; j < m; j++) {
    21             if (row[i])    v[i][j] = max(v[i][j], row[i]);
    22             if (column[j])    v[i][j] = max(v[i][j], column[j]);
    23             printf("%d%c", v[i][j], " 
    "[j == m - 1]);
    24         }
    25     
    26     return 0;
    27 }
    C

    D.正常地二叉搜索树去做会被有序卡成n方。考虑记录id后的数字排序,可以像中序遍历一样,先找到[l, r]中最早出现的那个,它的ans就是当前深度,然后两边进行分治解决。但如果暴力扫一遍去寻找最早出现的,还是n方的,所以用ST表预处理一下区间最小值。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn = 3e5 + 5;
     5 int N;
     6 long long Ans[maxn];
     7 struct V {
     8     int val, id;
     9     bool operator < (const V &rhs) const {
    10         return val < rhs.val;
    11     }
    12 }a[maxn];
    13 int ST[maxn][20], pos[maxn];
    14 
    15 void Pre() {
    16     for (int i = 1; i <= N; i++)
    17         ST[i][0] = a[i].id;
    18     for (int j = 1; (1 << j) <= N; j++) {
    19         for (int i = 1; i + (1 << j) - 1 <= N; i++) {
    20             ST[i][j] = min(ST[i][j - 1], ST[i + (1 << (j-1))][j - 1]);
    21         }
    22     }
    23     for (int i = 1; i <= N; i++)
    24         pos[a[i].id] = i;
    25 }
    26 
    27 int Query(int l, int r) {
    28     int j = log2(r - l + 1);
    29     return min(ST[l][j], ST[r - (1 << j) + 1][j]);
    30 }
    31 
    32 void Divide(int l, int r, int depth) {
    33     if (l > r)    return;
    34     
    35     int id = Query(l, r);
    36     Ans[id] = depth;
    37     Divide(l, pos[id] - 1, depth + 1);
    38     Divide(pos[id] + 1, r, depth + 1);
    39 }
    40 
    41 int main() {
    42     scanf("%d", &N);
    43     for (int i = 1; i <= N; ++i) {
    44         scanf("%d", &a[i].val);
    45         a[i].id = i;
    46     }
    47     sort(a + 1, a + 1 + N);
    48     Pre();
    49     Divide(1, N, 0);
    50     for (int i = 1; i <= N; ++i) {
    51         Ans[i] += Ans[i - 1];
    52         printf("%lld
    ", Ans[i]);
    53     }
    54     return 0;
    55 }
    D

    E.命题人本意是个裸的01分数规划,我读错题了,以为默认1是起点,其实是谁做起点都行只要有环路即可。因此就是二分里面套个判负环存在即可。

     1 #include <bits/stdc++.h>
     2 #define fi first
     3 #define se second
     4 using namespace std;
     5 
     6 typedef double db;
     7 const int maxn = 2005;
     8 const db eps = 1e-4;
     9 int n, m, vis[maxn];
    10 vector<pair<int, db>> adj[maxn];
    11 db dis[maxn];
    12 
    13 bool dfs(int cur, db ans) {
    14     vis[cur] = 1;
    15     for (auto i : adj[cur]) {
    16         if (dis[i.fi] > dis[cur] + i.se - ans) {
    17             dis[i.fi] = dis[cur] + i.se - ans;
    18             if (vis[i.fi] || dfs(i.fi, ans))
    19                 return vis[cur] = 0, 1;
    20         }
    21     }
    22     return vis[cur] = 0;
    23 }
    24 
    25 bool ok(db mid) {
    26     memset(dis, 0, sizeof(dis));
    27     for (int i = 1; i <= n; i++)
    28         if (dfs(i, mid))
    29             return 1;
    30     return 0;
    31 }
    32 
    33 int main() {
    34     scanf("%d %d", &n, &m);
    35     for (int i = 0; i < m; i++) {
    36         int u, v; db c;
    37         scanf("%d %d %lf", &u, &v, &c);
    38         adj[u].push_back({v, c});
    39     }
    40     db l = 0, r = 1e9 + 5;
    41     while (r - l > eps) {
    42         db mid = (l + r) / 2.0;
    43         if (ok(mid))    r = mid;
    44         else    l = mid;
    45     }
    46     if (l <= 1e9)    printf("%.2lf
    ", l);
    47     else    puts("Rinne is cute");
    48 }
    E

    F.树根和树叶不连通的最小代价,dfs并且dp处理一下,选min(cur-fa的cost, son的和)。

     1 #include <bits/stdc++.h>
     2 #define pb push_back
     3 #define fi first
     4 #define se second
     5 using namespace std;
     6 
     7 typedef long long ll;
     8 typedef pair<int, ll> pil;
     9 const int maxn = 1e5 + 5;
    10 int N, M, S;
    11 vector<pil> adj[maxn];
    12 ll f[maxn];
    13 
    14 void dfs(int cur, int fa) {
    15     ll res = 0LL;
    16     for (auto i : adj[cur]) {
    17         if (i.fi == fa) {
    18             if (cur != S)    f[cur] = i.se;
    19             continue;
    20         }
    21         dfs(i.fi, cur);
    22         res += f[i.fi];
    23     }
    24     if (cur == S || (adj[cur].size() > 1 && f[cur] > res))    f[cur] = res;
    25 }
    26 
    27 int main() {
    28     scanf("%d %d %d", &N, &M, &S);
    29     for (int i = 0; i < M; i++) {
    30         int u, v;
    31         ll cost;
    32         scanf("%d %d %lld", &u, &v, &cost);
    33         adj[u].pb({v, cost});
    34         adj[v].pb({u, cost});
    35     }
    36     dfs(S, 0);
    37     printf("%lld
    ", f[S]);
    38     return 0;
    39 }
    F

    G.每读入一个数,二进制枚举其素因子(不超过7个)的组合,容斥修改答案即可。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn = 2e5 + 5, maxa = 5e5 + 5;
     5 int N, opt, x, ans;
     6 int primes[maxa], v[maxa], tot;
     7 int num[maxa];
     8 bool in[maxa];
     9 
    10 void pre() {
    11     for (int i = 2; i < maxa; i++) {
    12         if (!v[i]) {
    13             primes[tot++] = i;
    14             v[i] = i;
    15         }
    16         for (int j = 0; j < tot && (long long)primes[j] * i < maxa; j++) {
    17             v[primes[j] * i] = primes[j];
    18             if (i % primes[j] == 0)    break;
    19         }
    20     }
    21 }
    22 
    23 int main() {
    24     pre();
    25     scanf("%d", &N);
    26     while (N--) {
    27         scanf("%d %d", &opt, &x);
    28         if (opt == 1 && in[x])    continue;
    29         if (opt == 2 && !in[x])    continue;
    30         if (opt == 3) {
    31             printf("%d
    ", ans);
    32             continue;
    33         }
    34         int que[10], tail = 0, t = x;
    35         while (v[t]) {
    36             int k = v[t];
    37             que[tail++] = k;
    38             while (t % k == 0)    t /= k;
    39         }
    40         for (int i = 0; i < (1 << tail); i++) {
    41             int tmp = 1, cnt = 0;
    42             for (int j = 0; j < tail; j++) {
    43                 if ((1 << j) & i) {
    44                     cnt++;
    45                     tmp *= que[j];
    46                 }
    47             }
    48             if (opt == 1) {
    49                 ans += cnt & 1 ? -num[tmp] : num[tmp];
    50                 num[tmp]++;
    51             } else {
    52                 num[tmp]--;
    53                 ans += cnt & 1 ? num[tmp] : -num[tmp];
    54             }
    55         }
    56         in[x] ^= 1;
    57     }
    58     return 0;
    59 }
    G

     G的方法二是式子稍微改一改弄成莫比乌斯函数的形式,然后枚举当前读入x的因数d,视为x是d的倍数,更新答案即可。factor不能预处理会被卡掉。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn = 2e5 + 5, maxa = 5e5 + 5;
     5 int N, opt, x, ans;
     6 int primes[maxa], mu[maxa], tot;
     7 int num[maxa];
     8 bool in[maxa], vis[maxa];
     9 
    10 void pre() {
    11     mu[1] = 1;
    12     for (int i = 2; i < maxa; i++) {
    13         if (!vis[i]) {
    14             primes[tot++] = i;
    15             mu[i] = -1;
    16         }
    17         for (int j = 0; j < tot && (long long)primes[j] * i < maxa; j++) {
    18             vis[primes[j] * i] = true;
    19             if (i % primes[j] == 0)    break;
    20             mu[primes[j] * i] = -mu[i];
    21         }
    22     }
    23 }
    24 
    25 void Update(int i, int opt) {
    26     ans -= (long long)num[i] * (num[i] - 1) / 2 * mu[i];
    27     num[i] += opt == 1 ? 1 : -1;
    28     ans += (long long)num[i] * (num[i] - 1) / 2 * mu[i];
    29 }
    30 
    31 int main() {
    32     pre();
    33     scanf("%d", &N);
    34     while (N--) {
    35         scanf("%d %d", &opt, &x);
    36         if (opt == 1 && in[x])    continue;
    37         if (opt == 2 && !in[x])    continue;
    38         if (opt == 3) {
    39             printf("%d
    ", ans);
    40             continue;
    41         }
    42         for (int i = 1; i * i <= x; i++) {
    43             if (x % i == 0) {
    44                 Update(i, opt);
    45                 if (x / i != i)    Update(x / i, opt);
    46             }
    47         }
    48         in[x] ^= 1;
    49     }
    50     return 0;
    51 }
    Gmu函数版

    H.边权只有三种情况所以拆点跑最短路即可。

     1 #include <bits/stdc++.h>
     2 #define pb push_back
     3 #define fi first
     4 #define se second
     5 using namespace std;
     6 
     7 typedef double db;
     8 typedef pair<db, int> pdi;
     9 const db INF = 1e18;
    10 int N, M, st, ed;
    11 vector<pdi> adj[300005];
    12 db dis[300005];
    13 
    14 db dij(int st, int ed) {
    15     for (int i = st; i <= ed; i++)    dis[i] = INF;
    16     dis[st] = 0.0;
    17     priority_queue<pdi, vector<pdi>, greater<pdi>> Q;
    18     Q.push({0.0, st});
    19     while (Q.size()) {
    20         auto x = Q.top(); Q.pop();
    21         if (dis[x.se] < x.fi)    continue;
    22         for (auto i : adj[x.se]) {
    23             if (dis[i.se] > dis[x.se] + i.fi) {
    24                 dis[i.se] = dis[x.se] + i.fi;
    25                 Q.push({dis[i.se], i.se});
    26             }
    27         }
    28     }
    29     return dis[ed];
    30 }
    31 
    32 void add(int u, int v, db cost) {
    33     adj[u].pb({fabs(cost), v + N});
    34     adj[u + N].pb({fabs(1.0 / (1.0 - cost)), v + N * 2});
    35     adj[u + N * 2].pb({fabs(1 - 1.0 / cost), v});
    36 }
    37 
    38 int main() {
    39     scanf("%d %d", &N, &M);
    40     for (int i = 0; i < M; i++) {
    41         int u, v; db cost;
    42         scanf("%d %d %lf", &u, &v, &cost);
    43         add(u, v, cost); add(v, u, cost);
    44     }
    45     st = 0, ed = N * 3 + 1;
    46     adj[st].pb({0.0, 1});
    47     for (int i = 0; i < 3; i++) {
    48         adj[N + N * i].pb({0.0, ed});
    49     }
    50     
    51     db ans = dij(st, ed);
    52     if (ans < INF)    printf("%.3lf
    ", ans);
    53     else    puts("-1");
    54     return 0;
    55 }
    H

    I.第一步是把C的求法递推式改一改,是全部的A1~i、B1~i的异或和;第二步按位求贡献。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 typedef long long ll;
     5 const int mod = 1e9 + 7;
     6 int n;
     7 int cntA[2][32], cntB[2][32], Pow[32];
     8 
     9 int main() {
    10     scanf("%d", &n);
    11     vector<int> A(n), B(n);
    12     for (int i = 0; i < 32; i++) {
    13         Pow[i] = i ? 2LL * Pow[i - 1] % mod : 1;
    14     }
    15     for (int i = 0; i < n; i++) {
    16         scanf("%d", &A[i]);
    17     }
    18     for (int i = 0; i < n; i++) {
    19         scanf("%d", &B[i]);
    20     }
    21     for (int i = 0; i < n; i++) {
    22         int a = A[i], b = B[i], tmp = 0;
    23         for (int j = 0; j < 32; j++) {
    24             cntA[a % 2][j]++;
    25             cntB[b % 2][j]++;
    26             a /= 2, b /= 2;
    27             tmp = ((ll)tmp + (ll)Pow[j] * ((ll)cntA[0][j] * cntB[1][j] % mod + (ll)cntA[1][j] * cntB[0][j] % mod) % mod) % mod;
    28         }
    29         printf("%d ", tmp);
    30     }
    31     return 0;
    32 }
    I

     J.签到。唯一分解一下或者暴力sqrt都可以

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int T, n, a, b;
     5 
     6 void solve(int n) {
     7     vector<int> factor, cnt;
     8     for (int i = 2; i * i <= n; ++i) {
     9         if (n % i == 0) {
    10             factor.push_back(i);
    11             cnt.push_back(0);
    12             while (n % i == 0) {
    13                 n /= i;
    14                 cnt.back()++;
    15             }
    16         }
    17     }
    18     if (n > 1)    factor.push_back(n), cnt.push_back(1);
    19     
    20     a = 1, b = 1;
    21     for (int i = 0; i < factor.size(); i++) {
    22         int d = factor[i], p = cnt[i] / 2;
    23         a *= pow(d, p);
    24         if (cnt[i] & 1)    b *= d;
    25     }
    26 }
    27 
    28 int main() {
    29     for (scanf("%d", &T); T; T--) {
    30         scanf("%d", &n);
    31         if (n < 0)    puts("-1");
    32         else {
    33             solve(n);
    34             printf("%d %d
    ", a, b);
    35         }
    36     }
    37     return 0;
    38 }
    J
  • 相关阅读:
    Leetcode Binary Tree Preorder Traversal
    Leetcode Minimum Depth of Binary Tree
    Leetcode 148. Sort List
    Leetcode 61. Rotate List
    Leetcode 86. Partition List
    Leetcode 21. Merge Two Sorted Lists
    Leetcode 143. Reorder List
    J2EE项目应用开发过程中的易错点
    JNDI初认识
    奔腾的代码
  • 原文地址:https://www.cnblogs.com/AlphaWA/p/10651045.html
Copyright © 2011-2022 走看看