Loading

Codeforces Round #656 (Div. 3)

A. Three Pairwise Maximums

大意:

给出三个数xyz,问能否找到3个数abc,使得\(x=max(a,b), y=max(a,c) ,z=max(b,c)\)

思路:

如果任意两个数的最大值都相同,那么有解,否则无解

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t;
int main() {
    cin >> t;
    while (t--) {
        int x, y, z;
        cin >> x >> y >> z;
        if (max(x, y) == max(y, z) && max(x, y) == max(x, z)) {
            cout << "YES" << endl;
            cout << min({x, y, z}) << ' ' << min({x, y, z}) << ' ' << max({x, y, z}) << endl;
        } else
            cout << "NO" << endl;
    }
    return 0;
}

B. Restore the Permutation by Merger

大意:

给出一个长度为2n的数组,这个数组是由2个完全相同的1到n的全排列按照原本的顺序交叉组成的,问这个1到n的全排列是什么

思路:

每个数第一次遇到的时候输出即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t;
int vis[55];
int main() {
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        memset(vis, 0, sizeof vis);
        for (int i = 0; i < 2 * n; i++) {
            int x;
            cin >> x;
            if (vis[x] == 0) {
                vis[x] = 1;
                cout << x << ' ';
            }
        }
        cout << endl;
    }
    return 0;
}

C. Make It Good

大意:

给出一个数组,问最少删除多少个前缀,可以使得剩下的数组为一个好数组

好数组的定义是每次从数组的前面或者后面取出一个数,放到新数组里,全部取完后新数组为单调不减数组

思路:

好数组必然是一个先增后降的数组,所以从后往前找到这个区间即可

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, a[N];
int main() {
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        int mid = 0;
        for (int i = n - 1; i >= 1; i--) {
            if (a[i] > a[i - 1]) {
                mid = i - 1;
                break;
            }
        }
        int res = 0;
        if (mid != 0) {
            for (int i = mid; i >= 0; i--) {
                if (a[i] < a[i - 1]) {
                    res = i;
                    break;
                }
            }
        }
        cout << res << endl;
    }
    return 0;
}

D. a-Good String

大意:

c-Good 串的定义是满足一下条件之一的串:

如果长度为1,那么字符串等于c

如果长度大于1,那么左半边字符串的字符都是c,右半边的字符串是(c+1)-Good串

如果长度大于1,那么右半边字符串的字符都是c,左半边的字符串是(c+1)-Good串

现在给出一个长度为n的串,问最少修改多少次可以使得字符串为a-Good串

思路:

dp,先算z-good串,再算y-good串,最后算出a-good串即可

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;
typedef long long LL;
int t, n, l, dp[30][N];
string s;
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        cin >> s;
        n = log2(n);
        l = s.size();
        // cout << n << endl;
        if (n == 0) {
            cout << (s[0] != 'a') << endl;
            continue;
        }
        for (int i = 0; i < l; i++) {
            dp[0][i] = (s[i] != ('a' + n));
        }
        for (int i = 1; i <= n; i++) {
            int len = pow(2, i - 1);
            bool left = 1;

            for (int j = 0; j < l; j += len) {
                int base = 0;
                for (int k = j; k < j + len; k++) {
                    base += (s[k] != ('a' + n - i));
                }
                int minpre = 0x3f3f3f3f;
                if (left) {
                    for (int k = j + len; k < j + 2 * len;
                         k += max(1, len / 2)) {
                        minpre = min(minpre, dp[i - 1][k]);
                    }
                } else {
                    for (int k = j - len; k < j; k += max(1, len / 2)) {
                        minpre = min(minpre, dp[i - 1][k]);
                    }
                }
                dp[i][j] = minpre + base;
                left = !left;
            }
            /*
            for (int k = 0; k < l; k++) {
                cout << dp[i][k] << ' ';
            }
            cout << endl;
            */
        }
        cout << min(dp[n][0], dp[n][l / 2]) << endl;
    }
    return 0;
}

E. Directing Edges

大意:

给出n个点m条边的图,其中有的边为有向的,有的边为无向的,现在要求确定每个无向边的方向,使得最后获得的图中没有有向环。

思路:

对有向边进行拓扑排序,如果有环,那么必然无解

否则必然有解,因为可以将所有的无向边的方向设置为从拓扑序小的指向拓扑序大的,这样保证没有环

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, n, m, in[N];
struct node {
    int u, v;
} edge[N];
vector<int> mp[N];
int cnt, vis[N];
void bfs() {
    queue<int> q;
    for (int i = 1; i <= n; i++) {
        if (in[i] == 0) q.push(i);
    }
    while (!q.empty()) {
        int now = q.front();
        q.pop();
        vis[now] = ++cnt;
        for (int i = 0; i < mp[now].size(); i++) {
            int ne = mp[now][i];
            in[ne]--;
            if (in[ne] == 0) q.push(ne);
        }
    }
}

int main() {
    cin >> t;
    while (t--) {
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            in[i] = vis[i] = 0;
            mp[i].clear();
        }
        for (int i = 0; i < m; i++) {
            int p, x, y;
            cin >> p >> x >> y;
            if (p) mp[x].push_back(y), in[y]++;
            edge[i] = {x, y};
        }
        cnt = 0;
        bfs();
        if (cnt == n) {
            cout << "YES" << endl;
            for (int i = 0; i < m; i++) {
                int u = edge[i].u, v = edge[i].v;
                if (vis[u] < vis[v])
                    cout << u << ' ' << v << endl;
                else
                    cout << v << ' ' << u << endl;
            }
        } else
            cout << "NO" << endl;
    }
    return 0;
}

F. Removing Leaves

大意:

给出n个点的树,每次可以选择k个父节点相同的叶子,然后删掉他们,问一共能删几次

思路:

类似拓扑排序的方法,每次选择度为1的节点删掉,然后记录父节点删掉子节点的数量,如果父节点删掉的数量等于k,那么答案就+1

注意如果父节点删掉k个,且此时度为1,就要将其入队

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 5;
typedef long long LL;
int t, n, k;
vector<int> mp[N];
int in[N], del[N], res;
int vis[N];
void bfs() {
    queue<int> q;
    for (int i = 1; i <= n; i++) {
        if (in[i] == 1) q.push(i);
    }
    while (!q.empty()) {
        int now = q.front();
        q.pop();
        vis[now] = 1;
        for (int i = 0; i < mp[now].size(); i++) {
            int ne = mp[now][i];
            if (vis[ne]) continue;
            in[ne]--;
            del[ne]++;
            if (del[ne] == k) {
                del[ne] = 0;
                res++;
                if (in[ne] == 1) q.push(ne);
            }
        }
    }
}

int main() {
    cin >> t;
    while (t--) {
        cin >> n >> k;
        for (int i = 1; i <= n; i++) {
            mp[i].clear();
            in[i] = del[i] = vis[i] = 0;
        }
        for (int i = 1; i < n; i++) {
            int x, y;
            cin >> x >> y;
            mp[x].push_back(y);
            mp[y].push_back(x);
            in[x]++;
            in[y]++;
        }
        res = 0;
        bfs();
        cout << res << endl;
    }
    return 0;
}

G. Columns Swaps

大意:

给出一个2xn的矩阵,问能否通过交换某几列的第一行和第二行,使得第一行和第二行都是1到n的全排列

思路:

首先统计1到n出现的次数,必须都是两次否则输出-1

然后利用拓展域并查集,i代表这一列不换,i+n代表这一列需要变换

这样如果某个数出现在同一行,假设出现的位置分别为i和j,那么合并i和j+n,以及i+n和j

否则合并i和j i+n和j+n

这样最后会出现很多小联通块,这时候就选择i和i+n中数量较小的那一个即可

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;
typedef long long LL;
int t;
int a[N][2], n, cnt[N], pre[N], Size[2 * N], vis[2 * N], res[N], rescnt;
int f[2 * N];
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
    int fx = findf(x), fy = findf(y);
    if (fx != fy) {
        f[fx] = fy;
        Size[fy] += Size[fx];
    }
}
int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) cnt[i] = 0, pre[i] = 0;
        for (int i = 1; i <= n; i++) cin >> a[i][0], cnt[a[i][0]]++;
        for (int i = 1; i <= n; i++) cin >> a[i][1], cnt[a[i][1]]++;
        int flag = 1;
        for (int i = 1; i <= n; i++)
            if (cnt[i] != 2) {
                flag = 0;
            }
        if (flag == 0) {
            printf("-1\n");
            continue;
        }
        for (int i = 1; i <= n; i++) {
            f[i] = i, f[i + n] = i + n;
            Size[i] = 0, Size[i + n] = 1;
            vis[i] = vis[i + n] = 0;
        }
        for (int i = 0; i < 2; i++) {
            for (int j = 1; j <= n; j++) {
                int now = a[j][i];
                if (pre[now] == 0)
                    pre[now] = j;
                else {
                    if (pre[now] == j) continue;
                    int k = pre[now];
                    if (a[k][i] == now) {  //同一行
                        int fx = findf(k), fy = findf(j);
                        int fx2 = findf(k + n), fy2 = findf(j + n);
                        if (fx == fy)
                            flag = 0;
                        else
                            Union(fx, fy2), Union(fx2, fy);
                    } else {
                        int fx = findf(k), fy = findf(j);
                        int fx2 = findf(k + n), fy2 = findf(j + n);
                        if (fx == fy2)
                            flag = 0;
                        else
                            Union(fx, fy), Union(fx2, fy2);
                    }
                }
            }
        }
        for (int i = 1; i <= n; i++)
            if (findf(i + n) == findf(i)) {
                flag = 0;
                break;
            }
        if (flag == 0) {
            printf("-1\n");
            continue;
        }
        for (int i = 1; i <= n; i++) {
            int x = findf(i), y = findf(i + n);
            // if (x == i && y == (i + n)) continue;
            if (!vis[x] && !vis[y]) {
                if (Size[x] > Size[y])
                    vis[y] = 1;
                else
                    vis[x] = 1;
            }
        }
        rescnt = 0;
        for (int i = 1; i <= n; i++) {
            if (vis[f[i + n]]) res[rescnt++] = i;
        }
        printf("%d\n", rescnt);
        for (int i = 0; i < rescnt; i++) printf("%d ", res[i]);
        printf("\n");
    }
    return 0;
}
posted @ 2021-02-03 19:07  WinterFa1L  阅读(63)  评论(0编辑  收藏  举报