省选测试16

好久没有考场上 A题了

A 环

题目大意 : 构造出一个 01 序列使得存在一个 1 满足这个 1 后面是 0,且这个 1 和后面的 0 交换后与原序列循环同构

  • 你要移动一个数让他还是循环同构
  • 比如说样例 101011010101 移动了第4个1他就循环同构了, 因为每次都移动的是1,所以按1分开
101011010101
2  2  12  2  2  1
  • 移动一下
101010110101
2  2  2  12  2  1
  • 发现也就是让下面那个2212221循环同构,把这个换成01串
1101110
12  112
  • 移动
1110110
112  12
  • 发现还是让下面那个循环同构
  • 设一共n个数,让k个数为1,然后就发现下面那个序列就是一共k个数,让n%k个数为1,递归找就行
  • 找到了一个构造就好说了,我直接就枚举了整体移动多少位,然后暴力判断

Code

Show Code
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 105;

int a[N], b[N], p[N];

void Solve(int l, int k) {
    if (!k) return a[1] = -1, void();
    if (k == 1) return a[1] = 1, void();
    Solve(k, l % k);
    if (a[1] == -1) return;
    memcpy(b + 1, a + 1, k * 4);
    int cnt = 0, tmp = l / k;
    for (int i = 1; i <= k; ++i) {
        a[++cnt] = 1;
        for (int j = b[i] + tmp - 1; j; --j)
            a[++cnt] = 0;
    }
}

bool Judge(int l) {
    int cnt = 0;
    for (int i = 1; i <= l && cnt <= 2; ++i)
        if (a[i] != b[i]) p[++cnt] = i;
    int x = p[1], y = p[2];
    if (cnt != 2) return 0;
    if (x + 1 == y && b[x] && !b[y] && !a[x] && a[y]) return 1;
    if (x == 1 && y == l && !b[x] && b[y] && a[x] && !a[y]) return 1;
    return 0;
}

void Next(int l) {
    memcpy(b + 1, a + 1, l * 4);
    for (int d = 0; d < l; ++d) {
        for (int i = 1; i <= l; ++i)
            a[(i+d-1)%l+1] = b[i];
        if (Judge(l)) return;
    }
}

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        int l, k, n; 
        scanf("%d%d%d", &l, &k, &n);
        memset(a, 0, sizeof(a));
        Solve(l, k);
        if (a[1] == -1) puts("NO");
        else {
            puts("YES");
            while (n--) {
                for (int i = 1; i <= l; ++i)
                    printf("%d", a[i]);
                puts("");
                Next(l);
            }
        }
    }
    return 0;
}

B DNA序列

题目大意 : 给定 n 个字符串,每个串选一个前缀,以任意顺序拼起来,令得到的串字典序最小

  • 考虑依次区的部分分,很容易想到从前往后贪心的选择前缀,发现这样是不对的,正确的应该是从后往前贪。复杂度n的4次方

  • 然后就是看什么样的顺序可以贪心成功

  • 题解上就是找到一个最短的前缀x,使得x重复无限次后字典序比原串小

  • 然后找到一个后缀y,满足x不是y的前缀

  • 排序把x当作第一关键字从小到大排序,把y当作第二关键字从大到小排序,然后贪心就好了

Code

Show Code
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 55;

int n;
string ans;

struct Node {
    string s, x, y;
}a[N];

bool operator < (const Node &a, const Node &b) {
    return a.x != b.x ? a.x < b.x : a.y > b.y;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i].s; a[i].s += 'Z';
        int len = a[i].s.size(), x, y;
        for (x = 1; x <= len; ++x) {
            string tmp = a[i].s.substr(0, x), s;
            for (int j = 1; j <= 50; ++j) s += tmp;
            if (s < a[i].s) break;
        }
        a[i].x = a[i].s.substr(0, x); y = x;
        while (a[i].s.substr(y, x) == a[i].x) y += x;
        a[i].y = a[i].s.substr(y); 
        for (int j = 1; j <= 6; ++j)
            a[i].x += a[i].x;
    }
    sort(a + 1, a + n + 1);
    for (int i = n; i >= 1; --i) {
        int len = a[i].s.size();
        string lt = ans;
        ans = a[i].s[0] + ans;
        for (int j = 2; j <= len; ++j)
            ans = min(ans, a[i].s.substr(0, j) + lt);
    }
    cout << ans;
}

C 探寻

题目大意 : 给定一颗树,第一次经过一条边有花费,第一次到达一个点会有收益,问从根到终点最小透支多少

  • 首先把终点的收益设为inf,这样就可以去贪心的找了

  • 对每个节点按费用从小到大排序,如果收益是负数的话就放在后面

  • 考虑取出一个点 x,找到 x 的祖先中第一个没有被选过的点 fx

  • 如果 fx 不存在,说明 x 可以被直接选

  • 如果有 fx,就把 x 和 fx 合并到一个点

  • 如果 w[fx] > c[x], 说明走父节点所拿的钱足够走到 x,就让 fx 的收益加上 w[x] - c[x]

  • 否则走父节点的代价还会增多,就让 fx 的代价加上 c[x] - w[fx],fx 的收益就变成了 w[x]

Code

Show Code
#include <set>
#include <cstdio>

using namespace std;
const int N = 2e5 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar())
        if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar())
        x = x * 10 + c - '0';
    return x * f;
}

bool v[N];
int n, fa[N], f[N]; 
long long w[N], c[N], ans, sum;

int Find(int x) {
    return x == f[x] ? x : f[x] = Find(f[x]);
}

bool Cmp(int x, int y) {
    return (w[x] > c[x]) != (w[y] > c[y]) ? w[x] > c[x] : (c[x] != c[y] ? c[x] < c[y] : x < y);
}

set<int, bool(*)(int, int)> s(Cmp);

int main() {
    n = read(); v[0] = 1;
    for (int i = 1; i < n; ++i) {
        fa[i] = read(), w[i] = read(), c[i] = read();
        if (w[i] == -1) w[i] = 1e15;
        s.insert(i); f[i] = i;
    }
    while (!s.empty()) {
        int x = *s.begin(); s.erase(s.begin());
        v[x] = 1; f[x] = fa[x];
        int fx = Find(x);
        if (v[fx]) ans = min(ans, sum -= c[x]), sum += w[x];
        else {
            s.erase(s.find(fx));
            if (w[fx] > c[x]) w[fx] += w[x] - c[x];
            else c[fx] += c[x] - w[fx], w[fx] = w[x];
            s.insert(fx);
        }
    }
    printf("%lld\n", -ans);
    return 0;
}
posted @ 2021-02-18 19:35  Shawk  阅读(56)  评论(0编辑  收藏  举报