省选测试1

我好菜啊

A 翻转硬币

题目大意 : 给定每次可以翻的长度,有m种,问最后把这些硬币翻到指定状态的最少次数

  • 原题。翻转一段区间复杂度太大,如果用差分的话就只需要改变 l 和 r+1 处的值

  • 先把需要的状态转换为差分数组,翻转一段区间就会把两端的值(l和r+1)改掉,如果两端都是0的话没必要改,只有有1的时候才需要改

  • 把差分数组中为1的提出来,可以BFS预处理出来将两个点改掉需要的最小步数,进行DP

  • 把这些点的状态压为 s, 1表示还没消除,f[s]表示从全集到当前状态所需的最小步数,每次转移挑一个点和其它点匹配进行消除,最后f[0]即为答案

Code

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

const int N = 1e4 + 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;
}

int n, k, m, a[105], p[25], tot, d[N], c[25][25], q[N], l, r, f[1<<20|5];
bool b[N];

int main() {
    n = read() + 1; k = read(); m = read();
    for (int i = 1; i <= k; ++i) {
        int x = read();
        b[x] ^= 1; b[x+1] ^= 1;
    }
    for (int i = 1; i <= m; ++i)
        a[i] = read();
    for (int i = 1; i <= n; ++i)
        if (b[i]) p[++tot] = i;
    for (int i = 1; i <= tot; ++i) {
        memset(d, 0x3f, n * 4 + 4);
        d[p[i]] = 0; q[l=r=1] = p[i];
        while (l <= r) {
            int x = q[l++];
            for (int j = 1, y; j <= m; ++j) {
                if ((y = x + a[j]) <= n && d[y] > 1e9) 
                    d[y] = d[x] + 1, q[++r] = y;
                if ((y = x - a[j]) >= 1 && d[y] > 1e9) 
                    d[y] = d[x] + 1, q[++r] = y;
            }
        }
        for (int j = 1; j <= tot; ++j)
            c[i][j] = d[p[j]];
    }
    int s = (1 << tot) - 1;
    memset(f, 0x3f, s * 4);
    for (; s >= 1; --s) {
        int j;
        for (j = 1; !((s >> j - 1) & 1); ++j);
        for (int i = j + 1; i <= tot; ++i) {
            if (!((s >> i - 1) & 1)) continue;
            int t = s ^ ((1 << i - 1) + (1 << j - 1));
            f[t] = std::min(f[t], f[s] + c[i][j]);
        }
    }
    printf("%d\n", f[0] > 1e9 ? -1 : f[0]);
    return 0;
}

B 一起自习的日子 (Unaccepted)

题目大意 :

  • 咕咕咕

Code

Show Code

C Sanrd

题目大意 : 求一个排列的一个LIS 和 LDS,满足找出的两个子序列没有交集

  • 首先要知道一个排列的任意一个 LIS 和 LDS 最多只会有一个地方重合

  • 求出s[i]表示经过位置 i 的 lds 的数量,注意这个方案数会非常大,所以要对一个大质数取模

  • 然后找Lis,要满足经过的点的 s 的和不能等于 LDS 的总数 sum

  • 用树状数组维护

Code

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

const int N = 5e5 + 5, M = 1e9 + 7;

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;
}

int n, a[N], lis, lds, len1[N], len2[N], stk[N], tp, g[N];
int num1[N], num2[N], sum, s[N];
bool v[N];

struct Node1 {
    int m, num;
    Node1() { m = num = 0; }
}t1[N];

void Add1(int x, int w, int num) {
    for (; x <= n; x += x & -x) {
        if (w > t1[x].m) t1[x].m = w, t1[x].num= num;
        else if (w == t1[x].m && (t1[x].num += num) >= M) t1[x].num -= M; 
    }
}

Node1 Ask1(int x) {
    Node1 ans; ans.num = 1;
    for (; x; x -= x & -x) {
        if (t1[x].m > ans.m) ans = t1[x];
        else if (t1[x].m == ans.m && (ans.num += t1[x].num) >= M) ans.num -= M;
    }
    return ans;
}

struct Node2 {
    int m, p1, p2, n1, n2;
    Node2() {m = p1 = p2 = 0; n1 = n2 = -1;}
}t2[N], f[N];

void operator += (Node2 &a, const Node2 &b) {
    if (b.m > a.m) a = b;
    if (b.m == a.m && a.n2 == -1) {
        if (b.n1 != a.n1) a.n2 = b.n1, a.p2 = b.p1;
        else if (b.n2 != -1 && b.n2 != a.n1) a.n2 = b.n2, a.p2 = b.p2;
    }
}

void Add2(int x, int k, Node2 w) {
    w.p1 = w.p2 = k;
    for (; x <= n; x += x & -x) t2[x] += w;
}

Node2 Ask2(int x) {
    Node2 ans;
    for (; x; x -= x & -x) ans += t2[x];
    return ans;
}

void Add3(int x, int w, int k) {
    for (; x <= n; x += x & -x)
        if (w > t1[x].m) t1[x].m = w, t1[x].num = k;
}

Node1 Ask3(int x) {
    Node1 ans;
    for (; x; x -= x & -x)
        if (t1[x].m > ans.m) ans = t1[x];
    return ans;
}

int main() {
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 1; i <= n; ++i) {//LIS
        int x = Ask1(a[i]).m + 1; Add1(a[i], x, 0);
        lis = std::max(lis, x);
    }
    memset(t1 + 1, 0, sizeof(Node1) * n);
    for (int i = 1; i <= n; ++i) {//LDS ending with i
        Node1 x = Ask1(n - a[i] + 1); x.m++;
        Add1(n - a[i] + 1, len1[i] = x.m, num1[i] = x.num);
        lds = std::max(lds, len1[i]);
    }
    memset(t1 + 1, 0, sizeof(Node1) * n);
    for (int i = n; i >= 1; --i) {//LDS starting with i
        Node1 x = Ask1(a[i]); x.m++;
        Add1(a[i], len2[i] = x.m, num2[i] = x.num);
    }
    for (int i = 1; i <= n; ++i) {
        if (len1[i] + len2[i] == lds + 1) s[i] = 1ll * num1[i] * num2[i] % M;
        if (len1[i] == lds && (sum += num1[i]) >= M) sum -= M; 
    }
    for (int i = 1; i <= n; ++i) {//find the lis that meets the condition
        Node2 &x = f[i] = Ask2(a[i]); x.m++;
        if (x.n1 == -1) x.n1 = s[i];
        else if ((x.n1 += s[i]) >= M) x.n1 -= M;
        if (x.n2 != -1 && (x.n2 += s[i]) >= M) x.n2 -= M;
        Add2(a[i], i, x);
        if (x.m == lis && (x.n1 != sum || (x.n2 != -1 && x.n2 != sum))) {
            int p = i;
            while (p) {
                int last = stk[++tp] = p; v[p] = 1;
                p = sum != f[p].n1 ? f[p].p1 : f[p].p2;
                if ((sum -= s[last]) < 0) sum += M;
            }
            printf("%d\n", lis);
            while (tp) printf("%d ", stk[tp--]);
            memset(t1 + 1, 0, sizeof(Node1) * n);
            for (i = n; i >= 1; --i) {
                if (v[i]) continue;
                Node1 y = Ask3(a[i]); y.m++;
                g[i] = y.num;
                Add3(a[i], y.m, i);
                if (y.m == lds) {
                    printf("\n%d\n", lds);
                    for (int q = i; q; q = g[q])
                        printf("%d ", q);
                    return 0;
                }
            }
        }
    }
    puts("-1");
    return 0;
}
posted @ 2021-02-03 17:16  Shawk  阅读(52)  评论(0编辑  收藏  举报