• 暑假N天乐【比赛篇】 —— 2019牛客暑期多校训练营(第四场)


    再拖下去啥都学不了了,所以后面几场的补题应该只会出现赛场上开过的题,稍微减轻一下负担。

    以下题解包括:(A C D J K)

    (B) 题队友补了,线性基+线段树不想写了。

    比赛地址: https://ac.nowcoder.com/acm/contest/884#question

    【A】 meeting DFS+LCA

    队友AC的,雨我无瓜,大概就是求个树的直径就完事了

    给定 (n) 个点和 (n-1) 条边,有 (k) 个人分别在 (k) 个点上,问选择哪一个点作为相遇的点,能使得这些人相遇时间最短,输出相遇时间(每走一条边花一秒)。

    考虑距离最远的两个关键点,设它们的距离为 (dis)(lceil dis/2 ceil) 即为答案。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 1e5+5;
    
    vector<int> son[maxn];
    int n, k, dep[maxn], fa[maxn][20], in[maxn];
    int a[maxn];
    int b[maxn], c[maxn];
    
    void dfs(int pre, int rt) {
        dep[rt] = dep[pre] + 1;
        fa[rt][0] = pre;
        for(int i = 1; i < 20; i++) {
            fa[rt][i] = fa[fa[rt][i-1]][i-1];
        }
        for(int i = 0; i < son[rt].size(); i++) {
            if(son[rt][i] != pre)
                dfs(rt, son[rt][i]);
        }
    }
    
    int LCA(int x, int y) {
        if(dep[x] < dep[y]) {
            swap(x, y);
        }
        for(int i = 19; i >= 0; i--) {
            if(dep[x] - (1<<i) >= dep[y]) {
                x = fa[x][i];
            }
        }
        if(x == y) {
            return x;
        }
        for(int i = 19; i >= 0; i--) {
            if(fa[x][i] != fa[y][i]) {
                x = fa[x][i];
                y = fa[y][i];
            }
        }
        return fa[x][0];
    }
    
    int main() {
        while(~scanf("%d%d", &n, &k)) {
            for(int i = 1; i <= n; i++) {
                son[i].clear();
            }
            memset(in, 0, sizeof(in));
            for(int i = 0; i < n-1; i++) {
                int x, y;
                scanf("%d%d", &x, &y);
                son[x].push_back(y);
                son[y].push_back(x);
            }
            dep[0] = -1;
            int rt = 0;
            for(int i = 1; i <= n && rt == 0; i++) {
                if(in[i] == 0) {
                    rt = i;
                }
            }
            dep[1] = 0;
            dfs(1, rt);
            for(int i = 1; i <= k; i++){
                scanf("%d", &a[i]);
            }
            int lca, Max = 0, w = 1;
            for(int i = 2; i <= n; i++){
                lca = LCA(a[i], a[1]);
                int x = dep[a[i]]+dep[a[1]]-dep[lca]*2;
                if(x > Max){
                    Max = x;
                    w = i;
                }
            }
            Max = 0;
            for(int i = 1; i <= n; i++){
                if(i == w) continue;
                lca = LCA(a[w], a[i]);
                int x = dep[a[i]] + dep[a[w]]-dep[lca]*2;
                Max = max(Max, x);
            }
            printf("%d
    ", (Max+1)/2);
    
        }
        return 0;
    }
    

    【C】 sequence 线段树+单调栈

    给定两个长度为 (n) 的序列 (a)(b),选择一个区间 ([l, r] (1leq lleq rleq n)),使得 (max [min(a_{l...r})*max(b_{l...r})]) 。输出这个最大值。

    对于每个 (a_i),我们找到左边第一个比它小的元素 (a_l),右边第一个比它小的 (a_r),那么左端点在 ([l+1,i]),右端点在 ([i,r-1]) 的区间最小值就是 (a_i)。求l和r可以使用单调栈。

    得到 (a_i) 后,如果 (a_i>0) 我们就是要最大化 (sum(b[l..r]))。反之亦然。记 (b) 的前缀和为 (s),那么 (sum(b[l..r])=s[r]-s[l-1])。所以我们只要查询 ([i,r-1]) 最大的 (s)([l,i-1]) 最小的 (s),相减即可。查询区间最小值可以使用线段树。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const ll inf = 1e16;
    const int mod = 1e9 + 7;
    
    const int maxn = 3e6+5;
    
    int a[maxn], b[maxn];
    int l[maxn], r[maxn];   // 左(右)边第一个 < ai 的数的位置
    ll sum[maxn];
    ll MAX[maxn<<2], MIN[maxn<<2];
    
    void push_up(int rt) {
        MAX[rt] = max(MAX[rt<<1], MAX[rt<<1|1]);
        MIN[rt] = min(MIN[rt<<1], MIN[rt<<1|1]);
    }
    
    void build(int l, int r, int rt) {
        if(l == r) {
            MIN[rt] = MAX[rt] = sum[l];
            return ;
        }
        int mid = (l+r) >> 1;
        build(l, mid, rt<<1);
        build(mid+1, r, rt<<1|1);
        push_up(rt);
    }
    
    ll query_max(int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) {
            return MAX[rt];
        }
        int mid = (l+r) >> 1;
        ll ans = -inf;
        if(L <= mid) {
            ans = max(ans, query_max(L, R, l, mid, rt<<1));
        }
        if(R > mid) {
            ans = max(ans, query_max(L, R, mid+1, r, rt<<1|1));
        }
        return ans;
    }
    
    ll query_min(int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) {
            return MIN[rt];
        }
        int mid = (l+r) >> 1;
        ll ans = inf;
        if(L <= mid) {
            ans = min(ans, query_min(L, R, l, mid, rt<<1));
        }
        if(R > mid) {
            ans = min(ans, query_min(L, R, mid+1, r, rt<<1|1));
        }
        return ans;
    }
    
    int main() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        a[0] = a[n+1] = -(0x3f3f3f3f);
        sum[0] = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &b[i]);
            sum[i] = sum[i-1] + b[i];
        }
        build(0, n, 1);
        
        stack<int> s;
        s.push(0);
        for(int i = 1; i <= n; i++) {   // 找左边
            while(!s.empty() && a[s.top()] >= a[i]) {
                s.pop();
            }
            l[i] = s.top();
            s.push(i);
        }
        while(!s.empty()) {
            s.pop();
        }
        s.push(n+1);
        for(int i = n; i >= 1; i--) {
            while(!s.empty() && a[s.top()] >= a[i]) {
                s.pop();
            }
            r[i] = s.top();
            s.push(i);
        }
        ll ans = -inf;
        for(int i = 1; i <= n; i++) {
            // 左端点在[l+1,i],右端点在[i,r-1]的区间min就是 ai
            int l1 = l[i];
            int r1 = i-1;
            int l2 = i;
            int r2 = r[i]-1;
            if(a[i] < 0) {// 找最小
                ans = max(ans, (query_min(l2, r2, 0, n, 1) - query_max(l1, r1, 0, n, 1)) * a[i]);
            }
            else {
                ans = max(ans, (query_max(l2, r2, 0, n, 1) - query_min(l1, r2, 0, n, 1)) * a[i]);
            }
        }
        printf("%lld
    ", ans);
        return 0;
    }
    

    【D】 triples I 构造

    给定一个数 (n) ,用最少的3的倍数或运算成它,输出构成方案之一。

    (n \% 3 == 0) :直接输出即可

    (n \% 3 == 1)

    1. (n) 的二进制位有至少两个 (\% 3=1) 的,设为 (p)(q),取 ([a-p,a-q])即可
    2. 二进制位只有一个 (\% 3=1) 的,那么设 (\% 3=1) 的这个位为p,(\% 3=2) 的某个位为 (q),取 ([a-p,p+q])即可
    3. 二进制位没有 (\% 3=1) 的,那么假设有三个 (\% 3 = 2)的位 (p、q、r),取([a-p-q, p+q+r])即可

    (n \% 3 == 2) :和 1 的情况相同,把 1 和 2 互换即可

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 100+5;
    
    int a[maxn];
    ll m1[maxn], m2[maxn];
    
    int main() {
        int t;
        scanf("%d", &t);
        while(t--) {
            ll n;
            scanf("%lld", &n);
            if(n % 3 == 0) {
                printf("1 %lld
    ", n);
                continue;
            }
            ll temp = n;
            int cnt = 0;
            while(temp) {
                a[++cnt] = temp % 2;
                temp /= 2;
            }
            ll x = 1;
            int cnt1 = 0;
            int cnt2 = 0;
            for(int i = 1; i <= cnt; i++) {
                if(a[i]) {
                    if(x % 3 == 1) {
                        m1[++cnt1] = x; //mod 3 == 1
                    }
                    else if(x % 3 == 2) {
                        m2[++cnt2] = x; //mod 3 == 2
                    }
                }
                x = x*2;
            }
            if(n % 3 == 1) {
                if(cnt1 >= 2) {
                    printf("2 %lld %lld
    ", n - m1[1], n - m1[2]);
                }
                else if(cnt1 == 1) {
                    printf("2 %lld %lld
    ", n - m1[1], m1[1] + m2[1]);
                }
                else if(cnt2 >= 3) {
                    printf("2 %lld %lld
    ", m2[1] + m2[2] + m2[3], n - m2[1] - m2[2]);
                }
            }
            else if(n % 3 == 2) {
                if(cnt2 >= 2) {
                    printf("2 %lld %lld
    ", n - m2[1], n - m2[2]);
                }
                else if(cnt2 == 1) {
                    printf("2 %lld %lld
    ", n - m2[1], m1[1] + m2[1]);
                }
                else if(cnt1 >= 3) {
                    printf("2 %lld %lld
    ", m1[1] + m1[2] + m1[3], n - m1[1] - m1[2]);
                }
            }
        }
        return 0;
    }
    

    【J】 free 最短路dij变形

    给定一个 (n) 个点 (m) 条边的无向图,起点 (S) 和终点 (T) 固定,每条边都有 (cost),现在可以选择其中 (k) 条边的 (cost) 变成 0,问从起点到终点的最小 (cost)

    只要把最短路算法 (dijkstra)(dis) 数组变成二维,(dis[i][j]) 表示:到 (i) 点时候已经用了 (j) 条魔术边((i leq n , j leq k))。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 1e3+5;
    
    int vis[maxn][maxn];
    int head[maxn];
    int dis[maxn][maxn];
    int n, m, S, T, k;
    int tot;
    int ans;
    
    struct EDGE {
        int to, nxt, w;
    }edge[2*maxn];
    
    struct node {
        int v, w, k;
        bool operator < (const node &h)const {
            return w > h.w;
        }
    };
    
    void addedge(int u, int v, int w) {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    
    void dij() {
        priority_queue<node> q;
        q.push(node{S, 0, 0});
        while(!q.empty()) {
            node u = q.top();
            q.pop();
            int _k = u.k;
            int _v = u.v;
            if(vis[_v][_k] == 1)
                continue;
            vis[_v][_k] = 1;
            for(int i = head[_v]; ~i; i = edge[i].nxt) {
                int v = edge[i].to;
                int w = edge[i].w;
                if(dis[_v][_k] + w < dis[v][_k]) {
                    dis[v][_k] = dis[_v][_k] + w;
                    if(vis[v][_k] == 0)
                        q.push(node{v, dis[v][_k], _k});
                }
                if(_k < k && dis[_v][_k] < dis[v][_k+1]) {
                    dis[v][_k+1] = dis[_v][_k];
                    if(vis[v][_k+1] == 0)
                        q.push(node{v, dis[v][_k+1], _k+1});
                }           
            }
        }
    }
    
    int main() {
        while(~scanf("%d%d%d%d%d", &n, &m, &S, &T, &k)) {
            tot = 0;
            ans = inf;
            memset(head, -1, sizeof(head));
            memset(vis, 0, sizeof(vis));
            memset(dis, inf, sizeof(dis));
            for(int i = 0; i <= 1000; i++) {
                dis[S][i] = 0;
            }
            for(int i = 1; i <= m; i++) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                addedge(u, v, w);
                addedge(v, u, w);
            }
            dij();
            for(int i = 0; i <= k; i++)
                ans = min(dis[T][i], ans);
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    【K】 number 规律

    给定一个由数字组成的字符串,求出是300倍数的子串个数。0,00都算,并考虑前导零的情况。

    遍历一遍每次计算 (\%3)的值,如果本位和后一位都是 0,把之前统计的 (cnt[sum]) 的个数加上。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 1e5+5;
    
    char s[maxn];
    int cnt[3];
    
    int main() {
        while(~scanf("%s", s)) {
            memset(cnt, 0, sizeof(cnt));
            cnt[0] = 1;
            int n = strlen(s);
            ll ans = 0;
            int sum = 0;
            for(int i = 0; i < n; i++) {
                if(s[i] == '0' && s[i+1] == '0') {
                    ans = ans + cnt[sum];
                }
                if(s[i] == '0') {
                    ans ++;
                }
                sum = (sum + s[i]-'0') % 3;
                cnt[sum]++;
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    [模板] 循环数组的最大子段和
    [最短路][几何][牛客] [国庆集训派对1]-L-New Game
    [洛谷] P1866 编号
    1115 Counting Nodes in a BST (30 分)
    1106 Lowest Price in Supply Chain (25 分)
    1094 The Largest Generation (25 分)
    1090 Highest Price in Supply Chain (25 分)
    树的遍历
    1086 Tree Traversals Again (25 分)
    1079 Total Sales of Supply Chain (25 分 树
  • 原文地址:https://www.cnblogs.com/Decray/p/11287249.html
Copyright © 2020-2023  润新知