一个单根复杂度,空间线性的做法,以下默认块长为 n\sqrt{n}n 。
考虑对点按 aaa 为第一关键字排序后分块,对于每个块单独考虑。
思路即是每次考虑到了一个块,就把前缀块按 bbb 为第一关键字排序,把当前块也按 bbb 为第一关键字排序。然后扫描线,一个个考虑当前块的元素,将前缀块中 $b\leq $ 当前元素 bbb 的 ccc , ddd 塞进二维值域分块中,再对当前块的点查询 ccc 比他小的且 ddd 比他小的数的 dpdpdp 的最大值即可。
发现一个惊人的事实,前缀块的 ccc 与 ddd 中可以对当前块的 ccc 与 ddd 相对大小关系离散化,这样值域范围就是单根,二维值域分块即是插入 O(1)O(1)O(1) ,查询 O(n4⋅n4)=O(n)O(\sqrt[4]{n} \cdot \sqrt[4]{n})=O(\sqrt{n})O(4n⋅4n)=O(n)。因为做了 n\sqrt{n}n 次扫描线,每次 nnn 次插入,复杂度 O(nn)O(n\sqrt{n})O(nn)。共有 nnn 次查询,每次 O(n)O(\sqrt{n})O(n) ,复杂度 O(nn)O(n\sqrt{n})O(nn) 。
对于块内的转移直接暴力即可,每个点至多从块中的 n\sqrt{n}n 个元素转移,复杂度为 O(nn)O(n\sqrt{n})O(nn) 。
对于离散化,先对于块内排序,复杂度为均摊整体为 O(nlog2n)O(n\log_{2}{n})O(nlog2n) 。再对前缀块已经排好序的数列归并排序,只有 n\sqrt{n}n 次,单次 O(n)O(n)O(n) ,复杂度为 O(nn)O(n\sqrt{n})O(nn) 。
总结一下,我们得出了复杂度单根,空间 O(n)O(n)O(n) 的做法,我们因为发现前缀块的点能对当前块离散化而提出了此解法,可惜我自己实现的常数较大,如果有人有小常数解法,欢迎与我交流。
后来与其他人交流后发现有多种方法可以做到此复杂度,并且可能更好写或常数更小,但或许我这个方法也有自己的意义,就分享出来了。
#include <bits/stdc++.h>
using namespace std;
const int B=256,mb=16;
int mBid[B+10];
struct node{
int fen[mb+2][mb+2],a[B+10][B+10],fen1[mb+2][B+10],fen2[B+10][mb+2];
void Clear(){
memset(fen,0,sizeof(fen));
memset(a,0,sizeof(a));
memset(fen1,0,sizeof(fen1));
memset(fen2,0,sizeof(fen2));
}
void insert(int x,int y,int w){
a[x][y]=max(a[x][y],w);
fen1[mBid[x]][y]=max(fen1[mBid[x]][y],w);
fen2[x][mBid[y]]=max(fen2[x][mBid[y]],w);
fen[mBid[x]][mBid[y]]=max(fen[mBid[x]][mBid[y]],w);
}
int query(int x,int y){
int Max=0;
for(int i=x;i>=1;i--){
if(mBid[i]!=mBid[x]){
break;
}
for(int j=y;j>=1;j--){
if(mBid[j]!=mBid[y]){
break;
}
Max=max(Max,a[i][j]);
}
for(int j=mBid[y]-1;j>=1;j--){
Max=max(Max,fen2[i][j]);
}
}
for(int j=y;j>=1;j--){
if(mBid[j]!=mBid[y]){
break;
}
for(int i=mBid[x]-1;i>=1;i--){
Max=max(Max,fen1[i][j]);
}
}
for(int i=mBid[x]-1;i>=1;i--){
for(int j=mBid[y]-1;j>=1;j--){
Max=max(Max,fen[i][j]);
}
}
return Max;
}
}Kuai;
struct nodee{
int a,b,c,d,dp,W;
}A[50005],C[50005];
vector<int> lsha,lshb,lshc,lshd;
bool cmp(nodee aa,nodee bb){
if(aa.a==bb.a){
if(aa.b==bb.b){
if(aa.c==bb.c){
return aa.d<bb.d;
}
return aa.c<bb.c;
}
return aa.b<bb.b;
}
return aa.a<bb.a;
}
bool cmpp(nodee aa,nodee bb){
if(aa.b==bb.b){
if(aa.c==bb.c){
if(aa.d==bb.d){
return aa.a<bb.a;
}
return aa.d<bb.d;
}
return aa.c<bb.c;
}
return aa.b<bb.b;
}
int L[305],R[305],idB[50005],tot;
nodee num_yuan[50005],ti[50005],pai[50005],cun[50005];
bool cmpb(nodee aa,nodee bb){
return aa.b<bb.b;
}
bool cmpc(nodee aa,nodee bb){
return aa.c<bb.c;
}
bool cmpd(nodee aa,nodee bb){
return aa.d<bb.d;
}
int Id_num_c[50005],Id_num_d[50005];
int n;
void chuli_c(int id){
for(int i=1;i<=tot;i++){
for(int j=pai[i-1].c+1;j<=pai[i].c;j++){
Id_num_c[j]=i;
}
}
for(int i=pai[tot].c+1;i<=n;i++){
Id_num_c[i]=tot+1;
}
}
void chuli_d(int id){
for(int i=1;i<=tot;i++){
for(int j=pai[i-1].d+1;j<=pai[i].d;j++){
Id_num_d[j]=i;
}
}
for(int i=pai[tot].d+1;i<=n;i++){
Id_num_d[i]=tot+1;
}
}
int main(){
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
cin>>n;
for(int i=1;i<=B;i++){
mBid[i]=(i-1)/mb+1;
}
for(int i=1;i<=n;i++){
cin>>A[i].a>>A[i].b>>A[i].c>>A[i].d;
lsha.push_back(A[i].a);
lshb.push_back(A[i].b);
lshc.push_back(A[i].c);
lshd.push_back(A[i].d);
}
stable_sort(lsha.begin(),lsha.end());
stable_sort(lshb.begin(),lshb.end());
stable_sort(lshc.begin(),lshc.end());
stable_sort(lshd.begin(),lshd.end());
for(int i=1;i<=n;i++){
A[i].a=lower_bound(lsha.begin(),lsha.end(),A[i].a)-lsha.begin()+1;
A[i].b=lower_bound(lshb.begin(),lshb.end(),A[i].b)-lshb.begin()+1;
A[i].c=lower_bound(lshc.begin(),lshc.end(),A[i].c)-lshc.begin()+1;
A[i].d=lower_bound(lshd.begin(),lshd.end(),A[i].d)-lshd.begin()+1;
A[i].W=1;
}
stable_sort(A+1,A+n+1,cmp);
int N=0;
for(int i=1;i<=n;i++){
if(i==1 || A[i].a!=A[i-1].a || A[i].b!=A[i-1].b || A[i].c!=A[i-1].c || A[i].d!=A[i-1].d){
C[++N]=A[i];
}else{
C[N].W++;
}
}
for(int i=1;i<=N;i++){
A[i]=C[i];
A[i].dp=A[i].W;
}
n=N;
int Cnt=0;
for(int i=1;i<=n;i++){
idB[i]=(i-1)/B+1;
if(L[idB[i]]==0){
L[idB[i]]=i;
}
R[idB[i]]=i;
Cnt=(i-1)/B+1;
}
int Max=0;
for(int i=1;i<=Cnt;i++){
tot=0;
for(int j=L[i];j<=R[i];j++){
pai[++tot]=A[j];
cun[tot]=A[j];
}
int cnt_c=0,cnt_d=0;
stable_sort(pai+1,pai+tot+1,cmpc);
chuli_c(i);
int lst=0;
for(int j=1;j<=tot;j++){
cnt_c++;
if(j==1 || pai[j].c!=lst){
lst=pai[j].c;
pai[j].c=cnt_c;
}else{
lst=pai[j].c;
pai[j].c=pai[j-1].c;
}
}
stable_sort(pai+1,pai+tot+1,cmpd);
chuli_d(i);
lst=0;
for(int j=1;j<=tot;j++){
cnt_d++;
if(j==1 || pai[j].d!=lst){
lst=pai[j].d;
pai[j].d=cnt_d;
}else{
lst=pai[j].d;
pai[j].d=pai[j-1].d;
}
}
for(int j=1;j<L[i];j++){
num_yuan[j].c=Id_num_c[num_yuan[j].c];
num_yuan[j].d=Id_num_d[num_yuan[j].d];
// cout<<num_yuan[j].a<<" "<<num_yuan[j].b<<' '<<num_yuan[j].c<<' '<<num_yuan[j].d<<' '<<num_yuan[j].dp<<'\n';
}
// cout<<"xiaofenge\n";
stable_sort(pai+1,pai+tot+1,cmpp);
stable_sort(cun+1,cun+tot+1,cmpp);
int idl=1;
Kuai.Clear();
for(int j=1;j<=tot;j++){
while(idl<L[i] && num_yuan[idl].b<=pai[j].b){
Kuai.insert(num_yuan[idl].c,num_yuan[idl].d,num_yuan[idl].dp);
idl++;
}
cun[j].dp=max(cun[j].dp,cun[j].W+Kuai.query(pai[j].c,pai[j].d));
// cout<<cun[j].a<<' '<<pai[j].b<<' '<<pai[j].c<<' '<<pai[j].d<<' '<<pai[j].dp<<'\n';
}
for(int j=1;j<=tot;j++){
for(int k=1;k<j;k++){
if(cun[j].a>=cun[k].a && cun[j].b>=cun[k].b && cun[j].c>=cun[k].c && cun[j].d>=cun[k].d){
cun[j].dp=max(cun[j].dp,cun[k].dp+cun[j].W);
}
}
}
memcpy(num_yuan,ti,sizeof(num_yuan));
merge(num_yuan+1,num_yuan+L[i],cun+1,cun+tot+1,ti+1,cmpp);
memcpy(num_yuan,ti,sizeof(num_yuan));
// cout<<"ZHONGFEN\n";
// for(int j=1;j<=R[i];j++){
// cout<<num_yuan[j].a<<' '<<num_yuan[j].b<<' '<<num_yuan[j].c<<" "<<num_yuan[j].d<<' '<<num_yuan[j].dp<<'\n';
// }
// cout<<"Dafenge\n";
}
Max=0;
for(int i=1;i<=n;i++){
Max=max(Max,num_yuan[i].dp);
}
cout<<Max;
return 0;
}
/*
13
10 5 9 9
12 10 1 13
13 13 6 1
3 7 1 5
9 8 7 8
6 9 4 11
8 1 11 6
5 3 1 10
7 2 12 11
10 5 8 3
2 11 5 2
4 4 12 4
1 11 10 7
*/