zoukankan      html  css  js  c++  java
  • 暑假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;
    }
    
  • 相关阅读:
    Python:起步
    实战Django:Rango Part8
    实战Django:Rango Part7
    实战Django:Rango Part6
    实战Django:Rango Part5
    实战Django:Rango Part4
    Fitnesse集成TestLink
    oracle存储过程代码覆盖率统计工具
    ant svn
    Jenkins中关于一些插件的使用方法
  • 原文地址:https://www.cnblogs.com/Decray/p/11287249.html
Copyright © 2011-2022 走看看