衡八联考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
Copy
#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
Copy
#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
Copy
posted @   Shawk  阅读(63)  评论(0编辑  收藏  举报
编辑推荐:
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
阅读排行:
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· 语音处理 开源项目 EchoSharp
· 《HelloGitHub》第 106 期
· Huawei LiteOS基于Cortex-M4 GD32F4平台移植
· mysql8.0无备份通过idb文件恢复数据过程、idb文件修复和tablespace id不一致处
点击右上角即可分享
微信分享提示
CONTENTS