版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/81783129
题目大意:
给你4个长度为n的数组, 数组中每个元素都在1~n内, 且在同一个数组中每个元素最多重复2次, 求这4个数组的最长公共子序列。
题目思路:
考虑dp[i][j][k][l], 表示四个数组匹配到了相应位置的答案, 只有当i,j, k, l四个位置的值完全相同时才会产生加1的贡献。 考虑到每个值最多重复2次, 实质上状态数是8n级别的。 本质是求一个4位的最长偏序。
考虑使用kd-tree, 现将前3维做成节点存在kd-tree中, 按照剩下一维的顺序枚举, 每次处理完包含i的四元组在kd-tree中的询问后, 再更新kd-tree中的相应节点。 在kd-tree的询问过程中使用各种优化剪枝。
Code:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <cmath>
#include <vector>
#define ll long long
#define db double
#define fi first
#define se second
#define pi pair<int, int >
#define ls (x << 1)
#define rs ((x << 1) | 1)
#define mid ((l + r) >> 1)
#define mp(x, y) make_pair((x), (y))
#define pb push_back
using namespace std;
const int N = (int)1e4 + 10;
int a[N], b[N * 8];
vector<int > pos[3][N], vec[N]; int n;
struct Poi{
int d[3], val, id;
int mxd[3], mnd[3], max_val;
Poi(){}
Poi(int a, int b, int c, int i){
mxd[0] = mnd[0] = d[0] = a;
mxd[1] = mnd[1] = d[1] = b;
mxd[2] = mnd[2] = d[2] = c;
id = i;
}
}poi[N * 8]; int tot;
int rt, fa[N * 8], ch[N * 8][2];
int H;
bool cmp(const Poi &x, const Poi &y){
for (int i = 0; i < 3; i ++){
int j = (i + H) % 3;
if (x.d[j] != y.d[j]) return x.d[j] < y.d[j];
}
return 0;
}
void update(int x, int y){
for (int j = 0; j < 3; j ++){
poi[x].mxd[j] = max(poi[x].mxd[j], poi[y].mxd[j]);
poi[x].mnd[j] = min(poi[x].mnd[j], poi[y].mnd[j]);
}
}
int build(int l, int r, int k, int pre){
H = k;
fa[mid] = pre;
nth_element(poi + l, poi + mid, poi + r + 1, cmp);
if (l != mid) update(mid, ch[mid][0] = build(l, mid - 1, k + 1, mid));
if (mid != r) update(mid, ch[mid][1] = build(mid + 1, r, k + 1, mid));
//printf("%d %d %d\n", mid, ch[mid][0], ch[mid][1]);
return mid;
}
int qd[3], res;
bool in(int x){
for (int j = 0; j < 3; j ++)
if (qd[j] < poi[x].mxd[j]) return 0;
return 1;
}
bool out(int x){
for (int j = 0; j < 3; j ++)
if (qd[j] < poi[x].mnd[j]) return 1;
return 0;
}
bool chk(int x){
for (int j = 0; j < 3; j ++)
if (qd[j] < poi[x].d[j]) return 0;
return 1;
}
void query(int x){
if (!x) return;
if (res >= poi[x].max_val) return;
if (out(x)) return;
if (in(x)) res = max(res, poi[x].max_val);
else {
if (chk(x)) res = max(res, poi[x].val);
query(ch[x][0]), query(ch[x][1]);
}
}
void modf(int x, int v){
poi[x].val = max(poi[x].val, v);
for (int p = x; p; p = fa[p]) poi[p].max_val = max(poi[p].max_val, poi[x].val);
}
int main(){
scanf("%d", &n);
for (int i = 0; i < 4; i ++)
for (int j = 1, x; j <= n; j ++){
scanf("%d", &x);
if (i < 3) pos[i][x].pb(j);
else a[j] = x;
}
for (int x = 1; x <= n; x ++)
for (int i = 0; i < pos[0][x].size(); i ++)
for (int j = 0; j < pos[1][x].size(); j ++)
for (int k = 0; k < pos[2][x].size(); k ++){
++ tot;
poi[tot] = Poi(pos[0][x][i], pos[1][x][j], pos[2][x][k], tot);
vec[x].pb(tot);
}
rt = build(1, tot, 0, 0);
for (int i = 1; i <= tot; i ++) b[poi[i].id] = i;
for (int i = 1; i <= n; i ++){
vector<pair<int, int> > dp;
for (int j = 0; j < vec[a[i]].size(); j ++){
int nd = b[vec[a[i]][j]];
res = 0;
for (int k = 0; k < 3; k ++) qd[k] = poi[nd].d[k] - 1;
query(rt);
dp.pb(mp(nd, res + 1));
/*if (i == 2 && qd[0] == 1 && qd[1] == 1 && qd[2] == 2){
printf("%d\n", res);
}*/
}
for (int j = 0; j < dp.size(); j ++){
modf(dp[j].fi, dp[j].se);
}
}
printf("%d\n", poi[rt].max_val);
return 0;
}