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\n", 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\n", 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\n");
        else {
            while (k) {
                cnt++;
                if (k & 1) {
                    if (u & (1 << (cnt - 1))) printf("lock %d\n", p[cnt]);
                    else     printf("unlock %d\n", p[cnt]);
                }
                k >>= 1;
            }
        }
        last = u;
    }
    puts("checkmate!");
    return 0;
}

 

上一篇:PAT乙级1037 在霍格沃茨找零钱(C语言)


下一篇:CoreData教程