衡八联考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