Codeforces Round #599 (Div. 2)

A - Maximum Square

题意:给 \(n\) 块宽度为 \(1\) 长度为 \(a_i\) 的木板,把这些木板拼在一起,求最大形成的正方形的边长。

题解:贪心,从大到小排序,然后找第一个满足 \(a_i<i\) 的位置break掉。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n, a[1005];

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int k;
    scanf("%d", &k);
    while(k--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n, greater<int>());
        int ans = 0;
        for(int i = 1; i <= n; ++i) {
            if(a[i] >= i)
                ans = i;
            else
                break;
        }
        printf("%d\n", ans);
    }
}

B1 - Character Swap (Easy Version)

题意:给两个互异的字符串 \(s\) 和 \(t\) ,必须选择某个 \(s[i]\) 和 \(t[j]\) 交换恰好1次,问能否使得两个字符串相等。

题解:交换恰好1次应该是最多减少两个不等的位置,找出不等的位置之后判断是不是恰好只有两个,若是,则直接交换然后判断相等,否则直接No。

注:由于是互异的所有不用特判0,否则0是一定合法的(交换(1,1))。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n;
char s[10005], t[10005];

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int k;
    scanf("%d", &k);
    while(k--) {
        scanf("%d%s%s", &n, s + 1, t + 1);
        vector<int> dif;
        for(int i = 1; i <= n; ++i) {
            if(s[i] != t[i]) {
                dif.push_back(i);
                if(dif.size() >= 3)
                    break;
            }
        }
        if(dif.size() != 2) {
            puts("No");
        } else {
            swap(s[dif[0]], t[dif[1]]);
            if(strcmp(s + 1, t + 1) == 0)
                puts("Yes");
            else
                puts("No");
        }
    }
}

B2 - Character Swap (Hard Version)

题意:给两个互异的字符串 \(s\) 和 \(t\) ,选择某个 \(s[i]\) 和 \(t[j]\) 交换不超过 \(2n\) 次,问能否使得两个字符串相等。

题解:很显然有解的必要条件就是所有的字母出现都是偶数次,先判断这个。其次满足上面的必要条件之后必定有解,因为使用最多2次交换就可以消除至少1个位置上的不同。具体的做法是:固定已经相等的前半部分,然后假如 \(s[i] \neq t[i]\),如果有某个后面的 \(s[id]=s[i]\) ,那么就把 \(s[id]\) 和 \(t[i]\) 交换。否则,必定存在某个后面的 \(t[id]=s[i]\) ,但是 \(t[id]\) 不能直接与 \(t[i]\) 交换,所以让它与 \(s[n]\) 交换(这样比较好写),然后把 \(s[n]\) 和 \(t[i]\) 交换。

注:输出要先输出 \(i\) 再输出 \(j\) ,这个WA不值得。用set实现的这个算法可以应对5e4级别的,可惜数据量太小了,各路神仙直接暴力。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n;
char ss[55], tt[55];
int s[55], t[55];
set<int> accs[128], acct[128];
vector<pair<int, int> > op;

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int k;
    scanf("%d", &k);
    while(k--) {
        scanf("%d%s%s", &n, ss + 1, tt + 1);
        for(int c = 'a'; c <= 'z'; ++c) {
            accs[c].clear();
            acct[c].clear();
        }
        for(int i = 1; i <= n; ++i) {
            s[i] = (int)ss[i];
            t[i] = (int)tt[i];
            accs[s[i]].insert(i);
            acct[t[i]].insert(i);
        }
        bool suc = 1;
        for(int c = 'a'; c <= 'z'; ++c) {
            if((accs[c].size() + acct[c].size()) & 1) {
                suc = 0;
                break;
            }
        }
        if(suc) {
            op.clear();
            for(int i = 1; i <= n; ++i) {
                accs[s[i]].erase(i);
                if(s[i] != t[i]) {
                    if(accs[s[i]].size()) {
                        int id = *accs[s[i]].begin();
                        accs[s[id]].erase(id);
                        accs[t[i]].insert(id);
                        acct[t[i]].erase(i);
                        acct[s[id]].insert(i);
                        op.push_back({id, i});
                        swap(s[id], t[i]);
                    } else {
                        int id = *acct[s[i]].begin();
                        accs[s[n]].erase(n);
                        accs[t[id]].insert(n);
                        acct[t[id]].erase(id);
                        acct[s[n]].insert(id);
                        op.push_back({n, id});
                        swap(t[id], s[n]);

                        id = *accs[s[i]].begin();
                        accs[s[id]].erase(id);
                        accs[t[i]].insert(id);
                        acct[t[i]].erase(i);
                        acct[s[id]].insert(i);
                        op.push_back({id, i});
                        swap(s[id], t[i]);
                    }
                }
                acct[t[i]].erase(i);
            }
            puts("Yes");
            printf("%d\n", (int)op.size());
            for(auto i : op)
                printf("%d %d\n", i.first, i.second);

        } else
            puts("No");
    }
}

C - Tile Painting

题意:给排成一行的 \(n\) 个格子,要求所有的 \(i,j\) 满足 \(|i-j|\) 是 \(n\) 的因子的,都要同色,求最大的涂色数。

题解:非常显然就是所有质因子的gcd,当然因为都是质因子所有直接判断有没有多种然后输出 \(1\) 就可以,为什么要放在C题坑人,放在B题不好吗。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n;

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    while(~scanf("%lld", &n)) {
        if(n == 1) {
            puts("1");
            continue;
        }
        ll cn = n;
        vector<ll> fac;
        for(ll i = 2; i * i <= cn; ++i) {
            if(cn % i == 0) {
                fac.push_back(i);
                while(cn % i == 0)
                    cn /= i;
            }
        }
        if(cn != 1)
            fac.push_back(cn);
        if(fac.size() >= 2)
            puts("1");
        else
            printf("%lld\n", fac[0]);
    }
}

D - 0-1 MST

题意:给一个 \(n\) 个节点的完全图,有 \(m\) 条权为1的边,其他的边权为0,求最小生成树的权值和。

题解:改写Prim算法,放三个容器,一个权为无穷(未知),一个权为0,一个权为1,每次优先取出0容器里面的扩展,不消耗任何东西,否则取出1容器的扩展,cost++。扩展的时候1容器可以从无穷容器夺取节点,0容器可以从1容器夺取节点,所以1容器和无穷容器应该是set。复杂度想不明白。

注:换了好几次做法,最后应该是时间不够,浪费太多在前面换来换去了,应该是类似0-1BFS的思路,改写Prim算法。???这样魔改之后不就是0-1BFS了吗?就直接用0-1BFS就可以了。

补题细节:弄一个链表/并查集存放不在0容器里面的节点,遍历有序的边表时可以顺便知道哪些节点会进入0容器,哪些节点会变成1然后暂时放进1容器,吐出一个最小度点之后会删除这个链表/并查集中的大部分节点,最差情况是最小度点也是满的,也就是1e5的平方根的完全图,这种情况退化到2次方带log?但是数据小没问题(或者另外写一个算法解决这种情况),吐出点的顺序按度数来启发式吐,这样复杂度更玄学。

实验证明这样是对的。只有0-1边权的Prim实际上就和0-1BFS没有什么不同。主要是要避免遍历0边,解决的方法是记录还没有变成0边且有机会变成0边的节点,然后启发式吐出来一次尽可能把最多的0节点确定下来,复杂度玄学。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int  MAXN = 100005;

vector<int> G[MAXN];
int d[MAXN];

int dis[MAXN], vis[MAXN];
priority_queue<pair<int, int> > one, zero;

int pre[MAXN], nxt[MAXN];

int Prim(int s, int n) {
    int cnt = 0, cost = 0;
    dis[s] = 0;
    zero.push({-d[s], s});
    nxt[pre[s]] = nxt[s];
    pre[nxt[s]] = pre[s];
    while(cnt < n) {
        int u = -1;
        if(zero.size()) {
            u = zero.top().second;
            zero.pop();
        } else {
            u = one.top().second;
            one.pop();
            if(!vis[u])
                ++cost;
        }
        if(vis[u])
            continue;
        vis[u] = 1;
        ++cnt;

        int cur = nxt[0];
        for(int j = 0; j <= d[u]; ++j) {
            int v = G[u][j];
            while(v > cur) {
                if(dis[cur] != 0 && vis[cur] == 0) {
                    dis[cur] = 0;
                    zero.push({-d[cur], cur});
                    nxt[pre[cur]] = nxt[cur];
                    pre[nxt[cur]] = pre[cur];
                }
                cur = nxt[cur];
            }
            if(v == cur) {
                if(cur <= n && dis[cur] == INF) {
                    dis[cur] = 1;
                    one.push({-d[cur], cur});
                }
                cur = nxt[cur];
            }
        }
    }
    return cost;
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int n, m;
    while(~scanf("%d%d", &n, &m)) {
        while(one.size())
            one.pop();
        while(zero.size())
            zero.pop();
        for(int i = 1; i <= n; ++i) {
            G[i].clear();
            dis[i] = INF;
            vis[i] = 0;
        }
        for(int i = 0; i <= n; ++i) {
            pre[i] = i - 1;
            nxt[i] = i + 1;
        }
        while(m--) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        int s = 1;
        for(int i = 1; i <= n; ++i) {
            d[i] = G[i].size();
            sort(G[i].begin(), G[i].end());
            G[i].push_back(n + 1);
            if(d[i] < d[s])
                s = i;
        }
        printf("%d\n", Prim(s, n));
    }
}

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11809724.html

时间: 2024-10-06 01:47:58

Codeforces Round #599 (Div. 2)的相关文章

Codeforces Round #599 (Div. 2) D. 0-1 MST(bfs+set)

Codeforces Round #599 (Div. 2) D. 0-1 MST Description Ujan has a lot of useless stuff in his drawers, a considerable part of which are his math notebooks: it is time to sort them out. This time he found an old dusty graph theory notebook with a descr

Codeforces Round #599 (Div. 2)D 边很多的只有0和1的MST

题:https://codeforces.com/contest/1243/problem/D 分析:找全部可以用边权为0的点连起来的全部块 然后这些块之间相连肯定得通过边权为1的边进行连接 所以答案就是这些块的总数-1: #include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back const int M=1e5+5; set<int>s,g[M]; int vis[

Codeforces Round #599 (Div. 2) Tile Painting

题意:就是给你一个n,然后如果  n mod | i - j | == 0  并且 | i - j |>1 的话,那么i 和 j 就是同一种颜色,问你最大有多少种颜色? 思路: 比赛的时候,看到直接手推,发现有点东西,直接打表找出了规律 —— 如果 n的质因子只有一个,那么总数就是 那个 质因子.其它都为 1. 今天上课的时候无聊,还是试着推了一下原理. 1.如果一个数只有一个质因子 x ,那么  n-x .n-2x.n-3x ……等等全为一种颜色,也就是说每隔 x个就是同种颜色,这样的话就是有

C. Tile Painting (定理:任意一个合数都能够写成两个质数的乘积) 《Codeforces Round #599 (Div. 2) 》

Ujan has been lazy lately, but now has decided to bring his yard to good shape. First, he decided to paint the path from his house to the gate. The path consists of nn consecutive tiles, numbered from 11 to nn. Ujan will paint each tile in some color

Codeforces Round #599 (Div. 2) A. Maximum Square

Ujan decided to make a new wooden roof for the house. He has nn rectangular planks numbered from 11 to nn. The ii-th plank has size ai×1ai×1 (that is, the width is 11 and the height is aiai). Now, Ujan wants to make a square roof. He will first choos

Codeforces Round #599 (Div. 2) C. Tile Painting

Ujan has been lazy lately, but now has decided to bring his yard to good shape. First, he decided to paint the path from his house to the gate. The path consists of nn consecutive tiles, numbered from 11 to nn. Ujan will paint each tile in some color

Codeforces Round #599 (Div. 2) B2. Character Swap (Hard Version)

This problem is different from the easy version. In this version Ujan makes at most 2n2n swaps. In addition, k≤1000,n≤50k≤1000,n≤50 and it is necessary to print swaps themselves. You can hack this problem if you solve it. But you can hack the previou

Codeforces Round #599 (Div. 2) B1. Character Swap (Easy Version)

This problem is different from the hard version. In this version Ujan makes exactly one exchange. You can hack this problem only if you solve both problems. After struggling and failing many times, Ujan decided to try to clean up his house again. He

Codeforces Round #599 (Div. 2) B2. Character Swap (Hard Version) 构造

链接:https://www.luogu.com.cn/problem/CF1243B2 题意:给你长度为n的两个字符串s和t,你可以最多进行2*n次操作,每次操作选择i和j,然后交换s[i]和t[j],问你能否使得两个字符串相同 构造方法:假如(0~i)部分s和t已经相等,在i位置时首先在(i+1~t.size()-1)里找有没有和t[i]相同的字符,如果找到,则交换s[i]和t[j],如果找不到,则在s中找,找到之后,先将s[j]与t[t.size()-1],交换,再将s[i]与t[t.si