衡八联考2


线下评测文件名写的abc,连续第3天爆0了

A 最长公共子序列

题目大意 : 求4个序列的最长公共子序列,保证前3个序列每个序列不会有数字出现次数大于2

  • n4的dp很好些,f[i][j][k][l]表示a,b,c,d分别考虑到前i,j,k,l个,最长公共子序列的长度

  • 发现a[i]=b[j]=c[k]=d[l]的情况不多,所以有效转移只有8n个,转移是是从i,j,k,l都小的地方转移

  • 于是就是一个4维偏序优化的dp,可用CDQ+二维树状数组通过

Code

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

using namespace std;
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, p1[N][3], p2[N][3], p3[N][3], f[N*8], cnt, t[N][N], ans;

struct Node {
    int a, b, c, d, id;
}a[N*8], g[N*8];

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

void Add(int x, int y, int w) {
    for (int i = x; i <= n; i += i & -i)
        for (int j = y; j <= n; j += j & -j)
            t[i][j] = max(t[i][j], w);
}

int Ask(int x, int y, int ans = 0) {
    for (int i = x; i; i -= i & -i)
        for (int j = y; j; j -= j & -j)
            ans = max(ans, t[i][j]);
    return ans;
}

void Clear(int x, int y) {
    for (int i = x; i <= n; i += i & -i)
        for (int j = y; j <= n && t[i][j]; j += j & -j)
            t[i][j] = 0;
}

void Solve(int l, int r) {
    if (a[l].d == a[r].d) return;
    int mid = l + r >> 1;
    while (a[mid].d == a[l].d) mid++; mid--;
    while (a[mid].d == a[mid+1].d) mid--;
    Solve(l, mid); 
    memcpy(g + l, a + l, sizeof(Node) * (r - l + 1));
    sort(g + l, g + mid + 1); sort(g + mid + 1, g + r + 1);
    int i = l, j = mid + 1;
    for (; j <= r; ++j) {
        for (; i <= mid && g[i].a < g[j].a; ++i)
            Add(g[i].b, g[i].c, f[g[i].id]);
        f[g[j].id] = max(f[g[j].id], Ask(g[j].b - 1, g[j].c - 1) + 1);
    }
    for (--i; i >= l; --i) Clear(g[i].b, g[i].c);
    Solve(mid + 1, r);
}

int main() {
    freopen("lcs.in", "r", stdin);
    freopen("lcs.out", "w", stdout);
    n = read();
    for (int i = 1, x; i <= n; ++i) x = read(), (!p1[x][0] ? p1[x][0] : p1[x][1]) = i;
    for (int i = 1, x; i <= n; ++i) x = read(), (!p2[x][0] ? p2[x][0] : p2[x][1]) = i;
    for (int i = 1, x; i <= n; ++i) x = read(), (!p3[x][0] ? p3[x][0] : p3[x][1]) = i;
    for (int i = 1; i <= n; ++i) {
        int x = read();
        for (int j = 0; p1[x][j]; ++j)
            for (int k = 0; p2[x][k]; ++k)
                for (int l = 0; p3[x][l]; ++l)
                    a[++cnt] = (Node) {p1[x][j], p2[x][k], p3[x][l], i, cnt}, f[cnt] = 1;
    }
    Solve(1, cnt);
    for (int i = 1; i <= cnt; ++i)
        ans = max(ans, f[i]);
    printf("%d\n", ans);
    return 0;
}

B 排列

题目大意 : 求一个字典序最小的排列使得相邻位置值的异或值的最大值最小

  • 求出最小值和一组字典序不一定最小的方案好求

  • 从最高位开始考虑,如果每个数在这位都一样,那相邻异或值在这一位一定都是0,对于第一次出现不一样的位,答案一定不会小于这一位的值

  • 而答案其实就是这一位为0的数与这一位为1的数的异或值的最小值,

  • 然后就可以考虑每个位置可以放什么了

Code

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

using namespace std;
const int N = 3e5 + 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, a[N], cnt, sum, m, s[N], ans = 1e9, ch[N*30][2], trc, c[2], tot, lt, t[N];
set<int> b[2], p[N];
map<int, int> mp;

void Insert(int x) {
    int p = 0;
    for (int i = 29; i >= 0; --i) {
        bool k = x & 1 << i;
        if (!ch[p][k]) ch[p][k] = ++trc;
        p = ch[p][k];
    }
}

int Ask(int x) {
    int p = 0, ans = 0;
    for (int i = 29; i >= 0; --i) {
        bool k = x & 1 << i;
        if (ch[p][k]) p = ch[p][k];
        else p = ch[p][k^1], ans += 1 << i;
    }
    return ans;
}

bool Judge(int i) {
    int x = a[i]; bool k = x & m;
    s[mp[x]]--; c[k]--; tot -= s[mp[x^ans]];
    if (tot || !c[k] || !c[k^1]) {
        p[mp[x]].erase(i); b[k].erase(i);
        printf("%d ", i); lt = x; return 1;
    }
    s[mp[x]]++; c[k]++; tot += s[mp[x^ans]];
    return 0;
}

int main() {
    freopen("permutation.in", "r", stdin);
    freopen("permutation.out", "w", stdout);
    n = read(); sum = (1 << 30) - 1;
    for (int i = 1; i <= n; ++i)
        a[i] = read(), sum &= a[i];
    for (int i = 1; i <= n; ++i) {
        a[i] ^= sum; m = max(m, a[i]);
        if (!mp[a[i]]) mp[a[i]] = ++cnt;
        s[mp[a[i]]]++;
    }
    for (; m > (m & -m); m -= m & -m);
    for (int i = 1; i <= n; ++i)
        if (a[i] & m) Insert(a[i]);
    for (int i = 1; i <= n; ++i)
        if (!(a[i] & m)) ans = min(ans, Ask(a[i]));
    for (int i = 1; i <= n; ++i) {
        bool k = a[i] & m; 
        p[mp[a[i]]].insert(i); 
        b[k].insert(i); c[k]++;
        if (k) tot += s[mp[a[i]^ans]];
    }
    for (int i = 1; i <= n; ++i)
        if (Judge(i)) break;
    for (int i = 2; i <= n; ++i) {
        int cnt = 0; bool k = lt & m;
        if (s[mp[lt^ans]]) t[++cnt] = *p[mp[lt^ans]].begin();
        if (!b[k].empty()) t[++cnt] = *b[k].begin();
        if (b[k].size() > 1) t[++cnt] = *++b[k].begin();
        sort(t + 1, t + cnt + 1);
        for (int j = 1; j <= cnt; ++j)
            if (Judge(t[j])) break;
    }
    return 0;
}

C 加农炮 (Unaccepted)

题目大意 :

Code

Show Code
posted @ 2021-03-25 21:15  Shawk  阅读(60)  评论(0编辑  收藏  举报