Codeforces Round 1083 (Div. 2) (A~D)

前言

准备春招了,写写CF练练手,感觉这几题有点水,可能难度都在后面

A. Simons and Making It Beautiful

题意是最多交换一次,使得 \(i=max({r1,r2,…,ri})\) 尽可能小,把最大的数放在第一位就行。

int a[N], b[N];
void solve(){
    cin >> n;
    int mx = 0;
    int cnt = 0;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        if(a[i] == n) {
            swap(a[i], a[1]);
        }
    }
    for(int i =1 ; i <= n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
}

B. Simons and Cakes for Success

题意是找出最小的正整数 \(k\),使得 \(n\) 能整除 \(k^n\)
只要保证每个质因子都出现过,这样幂次上去,每个质因子都会出现 \(n\) 次,也就一定能被整除了。

void solve(){
    cin >> n;
    int sum = 1;
    for(int i = 2; i * i <= n; i++) {
        if(n % i == 0) {
            sum *= i;
            while(n % i == 0) {
                n /= i;
            }
        }
    }
    cout << sum * n << endl;
}

C. Simons and Posting Blogs

观察可以发现,在发布一个博客时候,这行越靠后的数,在序列\(Q\)中越靠前,同时先发布的数字会被后发布的相同数字覆盖。
因此每行可以倒过来存,同时只记录每个数字第一次出现。例如 \(3,6,4,3,1,1,5,4\) 可以看作 \(4, 5, 1, 3, 6\)
每次从还没发布的博客中,选出一个博客 \(i\),使得它贡献的字典序最小。对于其他未选中的博客,去掉已经出现过的数字。模拟即可。

int a[3001][3001];
void solve(){
    cin >> n;
    vector<vector<int>> s(n + 1);
    for(int i = 1; i <= n; i++) {
        cin >> a[i][0];
        map<int, int> flag;
        for(int j =  a[i][0]; j >= 1; j--) {
            cin >> a[i][j];
        }
        for(int j = 1; j <= a[i][0]; j++) {
            if (flag[a[i][j]] == 0) {
                flag[a[i][j]] = 1;
                s[i].push_back(a[i][j]);
            }
        }
    }
    map<int, int> flag;
    map<int, int> g;
    vector<int> ans;
    for (int i = 1; i <= n; i++) {
        int mi = -1;
        vector<int> best;
        for (int i = 1; i <= n; ++i) {
            if (flag[i]) continue;
            vector<int> cur;
            for (int x : s[i]) {
                if (!g[x]) cur.push_back(x);
            }
            if (mi == -1 || cur < best) {
                best = cur;
                mi = i;
            }
        }
        flag[mi] = true;
        for (int x : best) {
            ans.push_back(x);
            g[x] = true;
        }
    }
    for(auto i : ans) {
        cout << i << " ";
    }
    cout << endl;
}

D. Simons and Beating Peaks

题目要求数组中没有任何一个元素大于其相邻的两个元素,即不能有峰值。所以数组只能先单调递减,再单调递增、或者只单调递减、或只单调递增。
显然最大值 \(K\) 永远不可能被删除,那避免让\(K\)成为峰值,只能把\(K\)左侧或者右侧的数删光。于是我们有以下两种操作:

  • \(K\)成为最左端,把左侧的数删光,这要求\(K\)右侧至少有一个数。
  • 同理,让\(K\)成为最右端,把右侧的数删光。

固定好最大值之后,就可以在剩下的区域中固定次大值。一步步固定完所有数。
可以发现删数时,每次取左侧或者右侧的最大值的过程,可以用笛卡尔树解决。
把数组构建成笛卡尔树,然后进行树形\(DP\)即可。

int a[N];
int l[N], r[N], sz[N];
void dfs(int u)  {
    if (u == 0) return;
    sz[u] = 1;
    if (l[u]) {
        dfs(l[u]);
        sz[u] += sz[l[u]];
    }
    if (r[u]) {
        dfs(r[u]);
        sz[u] += sz[r[u]];
    }
};
int dfs2(int u) {
    if (u == 0 || sz[u] == 1) return 0;
    int ans = 1e9;
    if (r[u]) {
        ans = min(ans, sz[l[u]] + dfs2(r[u]));
    }
    if (l[u]) {
        ans = min(ans, sz[r[u]] + dfs2(l[u]));
    }
    return ans;
};
void solve() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        l[i] = r[i] = sz[i] = 0;
    }
    vector<int> st;
    for (int i = 1; i <= n; i++) {
        int lst = 0;
        while (!st.empty() && a[st.back()] < a[i]) {
            lst = st.back();
            st.pop_back();
        }
        if (!st.empty()) {
            r[st.back()] = i;
        }
        l[i] = lst;
        st.push_back(i);
    }
    int rt = st[0];
    dfs(rt);
    cout << dfs2(rt) << endl;
}

有帮助到你的话点个赞吧~

posted @ 2026-02-27 01:37  SHOJIG  阅读(12)  评论(0)    收藏  举报