zoukankan      html  css  js  c++  java
  • 2021牛客寒假算法基础集训营2

    整出来了七道题,ABC最后都没做赛后补的

    A题我和题解以及大部分人思路都不一样,并没有用到树状数组,我甚至看到有笛卡尔树做的(并不会),我是用了两次单调栈,维护两边最远的位置,再算贡献,然后这样写好像是代码长度最短的,应该是最简单的方法吧,但是这么写细节上也有很多坑,比如最小值不在区间内,在端点上时要特殊处理

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 10;
    int a[N], x[N];
    ll f[N][20], ans[N], dp[N], pre[N];
    ll gcd(ll a, ll b) {
        return b ? gcd(b, a % b) : a;
    }
    inline void cal(ll a, ll b) {
        ll p = gcd(a, b);
        a /= p;
        b /= p;
        printf("%lld/%lld
    ", a, b);
    }
    vector < int > v;
    stack < int > s; 
    int main() {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            f[i][0] = a[i];
        }
        for (int j = 1; j <= 20; j++)
            for (int i = 1; i + (1 << j) - 1 <= n; i++)
                f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        int m;
        scanf("%d", &m);
        while (m--) {
            int k;
            scanf("%d", &k);
            for (int i = 1; i <= k; i++) {
                scanf("%d", &x[i]);
                ans[a[x[i]]]++; v.push_back(a[x[i]]);
            }
            sort(x + 1, x + k + 1);
            s.push(0); dp[0] = (1 << 30);
            for (int i = 1; i < k; i++) {
                int l = log2(x[i + 1] - x[i] + 1);
                ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]);
                dp[i] = q;
                while (!s.empty() && dp[s.top()] <= q)
                    s.pop();
                pre[i] = (i - s.top());
                //ans[q] += 1ll * (i - s.top());
                s.push(i); 
            }
            while (!s.empty())    s.pop(); 
            s.push(k); dp[k] = (1 << 30);
            for (int i = k - 1; i >= 1; i--) {
                int l = log2(x[i + 1] - x[i] + 1);
                ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]);
                dp[i] = q; 
                while (!s.empty() && dp[s.top()] <= q)
                    s.pop();
                if (dp[i] == dp[i + 1])    ans[q] += 2 * pre[i];
                else ans[q] += 1ll * 2 * (s.top() - i) * pre[i];
                v.push_back(q);
                s.push(i); 
            }
            while (!s.empty())    s.pop();
            sort(v.begin(), v.end());
            for (int i = 0; i < v.size(); i++) {
                if (i != 0 && v[i] == v[i - 1])    continue;
                printf("%d ", v[i]);
                cal(ans[v[i]], 1ll * k * k);
                ans[v[i]] = 0;
            }
            v.clear();
        }
        return 0;
    }

    C题很简单,但是有一个坑就是长度大于l/2时候要判断,刚开始没有注意到这个,WA了好多次

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 10;
    int a[N], x[N];
    ll f[N][20], ans[N], dp[N], pre[N];
    ll gcd(ll a, ll b) {
        return b ? gcd(b, a % b) : a;
    }
    inline void cal(ll a, ll b) {
        ll p = gcd(a, b);
        a /= p;
        b /= p;
        printf("%lld/%lld
    ", a, b);
    }
    vector < int > v;
    stack < int > s; 
    int main() {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            f[i][0] = a[i];
        }
        for (int j = 1; j <= 20; j++)
            for (int i = 1; i + (1 << j) - 1 <= n; i++)
                f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        int m;
        scanf("%d", &m);
        while (m--) {
            int k;
            scanf("%d", &k);
            for (int i = 1; i <= k; i++) {
                scanf("%d", &x[i]);
                ans[a[x[i]]]++; v.push_back(a[x[i]]);
            }
            sort(x + 1, x + k + 1);
            s.push(0); dp[0] = (1 << 30);
            for (int i = 1; i < k; i++) {
                int l = log2(x[i + 1] - x[i] + 1);
                ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]);
                dp[i] = q;
                while (!s.empty() && dp[s.top()] <= q)
                    s.pop();
                pre[i] = (i - s.top());
                //ans[q] += 1ll * (i - s.top());
                s.push(i); 
            }
            while (!s.empty())    s.pop(); 
            s.push(k); dp[k] = (1 << 30);
            for (int i = k - 1; i >= 1; i--) {
                int l = log2(x[i + 1] - x[i] + 1);
                ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]);
                dp[i] = q; 
                while (!s.empty() && dp[s.top()] <= q)
                    s.pop();
                if (dp[i] == dp[i + 1])    ans[q] += 2 * pre[i];
                else ans[q] += 1ll * 2 * (s.top() - i) * pre[i];
                v.push_back(q);
                s.push(i); 
            }
            while (!s.empty())    s.pop();
            sort(v.begin(), v.end());
            for (int i = 0; i < v.size(); i++) {
                if (i != 0 && v[i] == v[i - 1])    continue;
                printf("%d ", v[i]);
                cal(ans[v[i]], 1ll * k * k);
                ans[v[i]] = 0;
            }
            v.clear();
        }
        return 0;
    }

    B题应该是最有意思的一道题了,并没有想出来怎么做,看到题解的时候感叹自己怎么这么菜,简单来说就是对每个状态建一个最短路树,然后像分层图一样连接起来,dfs一遍就可以了

    记一下自己的很多坑,一直只通过了90%

    1. 编号最小,需要用dis数组记录判断

    2. 每次可以操纵多个开关

    写的不是很优雅

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 110, M = 110 * (1 << 11);
    int p[11];
    int n, m, k;
    #define debug(x) cout << #x << " " << x << endl;
    vector < int > edge[N], G[M];
    inline void add(int u, int v) {
        edge[u].push_back(v);
    }
    int vis[M];
    bool col[M][2];
    queue < int > Q; 
    inline void build(int flag) {
        int cnt = 0, b = flag;
        while (b) {
            cnt++; 
            if (b & 1)    vis[p[cnt] + flag * n] = (1 << 30);
            b >>= 1;
        }
        for (int j = 0; j < (1 << k); j++)
            if (j != flag) {
                for (int i = 1; i <= n; i++)
                    G[j * n + i].push_back(flag * n + i);
            }
        if (vis[n + flag * n] == (1 << 30)) return ;
        Q.push(n); vis[n + flag * n] = 0;
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            for (int i = 0; i < edge[u].size(); i++) {
                int v = edge[u][i];
                if (vis[v + flag * n] > vis[u + flag * n] + 1) {
                    G[v + flag * n].push_back(u + flag * n);
                    if (vis[v + flag * n] != (1 << 30))    Q.push(v);
                    vis[v + flag * n] = vis[u + flag * n] + 1;
                }
                else if (vis[v + flag * n] == vis[u + flag * n] + 1)
                    G[v + flag * n].back() = min(G[v + flag * n].back(), u + flag * n);
            }
        }
    }
    
    inline bool check(int u, int v) {
        return (u - 1) / n == (v - 1) / n;
    }
    
    stack < int > s;
    bool dfs(int u, int flag) {
        bool tmp = false;
        col[u][flag] = true;
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];
            if (check(u, v) || !flag) {
                if (!col[v][!check(u, v)] && dfs(v, !check(u, v))) {
                    s.push(u);
                    return true;
                }
                tmp = true;
            }
        }
        if (!tmp && u % n != 0) {
            s.push(u);
            return true;
        }
        return false;
    }
    
    int main() {
        scanf("%d %d %d", &n, &m, &k);
        for (int i = 1; i <= k; i++)
            scanf("%d", &p[i]);
        for (int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            add(u, v); add(v, u);
        }
        memset(vis, 0x3f, sizeof(vis));
        for (int i = 0; i < (1 << k); i++)
            build(i);
        //if (!dfs(1, 0)) return 0;
        dfs(1, 0);
        int last = (s.top() - 1) / n; s.pop();
        while (!s.empty()) {
            int u = s.top(); s.pop();
            u = (u - 1) / n;
            int k = last ^ u, cnt = 0;
            if (k == 0)
                printf("continue
    ");
            else {
                while (k) {
                    cnt++;
                    if (k & 1) {
                        if (u & (1 << (cnt - 1))) printf("lock %d
    ", p[cnt]);
                        else     printf("unlock %d
    ", p[cnt]);
                    }
                    k >>= 1;
                }
            }
            last = u;
        }
        puts("checkmate!");
        return 0;
    }
  • 相关阅读:
    juqery 点击分页显示,指定一页显示多少个,首次加载显示多少个
    PHP指定时间戳/日期加一天,一年,一周,一月
    POJ 2955 Brackets 区间合并
    zoj 3537 Cake 区间DP (好题)
    DP——最优三角形剖分
    LightOJ 1422 Halloween Costumes
    POJ 1738 石子合并2 GarsiaWachs算法
    NIOP1995 石子合并(区间DP)
    POJ 2429
    pollard_rho和Miller_Rabin
  • 原文地址:https://www.cnblogs.com/cminus/p/14400341.html
Copyright © 2011-2022 走看看