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
  • 相关阅读:
    php怎么实现多态?
    php怎么识别真实ip
    php析构函数什么时候调用?
    php解析xml的几种方式
    thinkPHP5框架路由常用知识点汇总
    用Python打造了一个渗透测试暴力探测器
    修复wecenter移动版description首页描述一样问题
    寒假小软件开发记录02--布局
    寒假小软件开发记录01--确定方向和素材准备
    大二上学期个人总结
  • 原文地址:https://www.cnblogs.com/AlphaWA/p/10651045.html
Copyright © 2011-2022 走看看