真是一个自闭的题目(调了一个上午+大半个下午)

从\(WA\)到\(WA+TLE\)到\(TLE\)到\(AC\)

真的艰辛。

首先,这个题,我们可以考虑直接上四维KDTree来解决。

对于kdtree上的每个节点,我们维护三个值,分别表示各个维度的\(mn\),当前节点的\(val\)(这个是用来每次更新\(ans\)的),子树的\(val\)的最大值(用来做估价函数)

首先\(build\)出整个kdtree

void up(int root)
{
for (int i=0;i<=3;i++)
{
if (t[root].l)
t[root].mn[i]=min(t[root].mn[i],t[t[root].l].mn[i]);
if (t[root].r)
t[root].mn[i]=min(t[root].mn[i],t[t[root].r].mn[i]);
}
t[root].val=max(t[root].cao,max(t[t[root].l].val,t[t[root].r].val));
} void build(int &x,int l,int r,int fa,int dd)
{
ymh = dd;
int mid = l+r >> 1;
dd++;
if (dd==4) dd=0;
x = mid;
nth_element(t+l,t+x,t+r+1);
t[x].fa=fa;
t[x].cao=0;
for (int i=0;i<=3;i++) t[x].mn[i]=t[x].d[i];
back[t[x].num]=x;
if (l<x) build(t[x].l,l,mid-1,mid,dd);
if (x<r) build(t[x].r,mid+1,r,mid,dd);
up(x);
}

然后,我们用kdtree来维护一个做lis的过程

其中,我们先对所有点进行排序,然后依次枚举他们。

对于一次\(query\),我们首先\(check\)一下当前点能不能更新\(ans\),然后通过子树max来估价,判断先进入哪个子树,或者进不进入当前子树

bool get(KD a,KD b)
{
for (int i=0;i<=3;i++)
if (a.d[i]>b.d[i]) return 0;
return 1;
}
int calc(int x,KD b)
{
if (!x) return 0;
for (int i=0;i<=3;i++)
if (t[x].mn[i]>b.d[i]) return 0;
return 1;
}
void query(int x,KD caonima,int dd)
{
if (!x) return ;
// cout<<x<<" "<<t[x].d[0]<<" "<<t[x].d[1]<<" "<<t[x].d[2]<<" "<<t[x].d[3]<<endl;
//cout<<"*** "<<t[x].l<<" "<<t[x].r<<endl;
int d1 = calc(t[x].l,now);
int d2 = calc(t[x].r,now);
int d=get(t[x],now);
if (d && tmp<t[x].cao) tmp=t[x].cao;
if (t[t[x].l].val>=t[t[x].r].val)
{
if (d1 && t[t[x].l].val>tmp) query(t[x].l,caonima,(dd+1)%4);
if (d2 && t[t[x].r].val>tmp) query(t[x].r,caonima,(dd+1)%4);
}
else
{
if (d2 && t[t[x].r].val>tmp) query(t[x].r,caonima,(dd+1)%4);
if (d1 && t[t[x].l].val>tmp) query(t[x].l,caonima,(dd+1)%4);
}
}

下面是这个题的重点

就是因为\(lis\),所以我们要修改当前点的\(val\)以及他子树内的\(val\),为了下一个点的统计。

这里我们修改的方式是找到这个点对应的kdtree上的节点,然后不停的暴力向上跳。

我们首先把这个对应节点的\(val\)弄成这次我们\(query\)的答案+1,然后依次修改祖先们的子树\(max\)

void upp(int x)
{
//t[x].val=max(t[x].val,t[x].cao);
t[x].val=max(t[x].cao,max(t[t[x].l].val,t[t[x].r].val));
}
void update(int x,int pp)
{
t[x].cao=pp;
upp(x);
x=t[x].fa;
while (x)
{
//cout<<"****"<<t[x].d[0]<<" "<<t[x].d[1]<<" "<<t[x].d[2]<<" "<<t[x].d[3]<<endl;
upp(x);
x=t[x].fa;
}
}

那么其实这个题就应该差不多了

不得不说细节真的是很多的qwq

具体还是看代码吧

kdtree真神奇!

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e5+1e2;
struct KD{
int mn[4];
int val;
int l,r,fa;
int d[4];
int num;
int cao;
};
KD t[maxn],now;
int a[maxn];
int n,m,root,ans,tmp,mval;
int back[maxn];
int ymh;
bool operator<(KD a,KD b)
{
return a.d[ymh]<b.d[ymh];
}
void up(int root)
{
for (int i=0;i<=3;i++)
{
if (t[root].l)
t[root].mn[i]=min(t[root].mn[i],t[t[root].l].mn[i]);
if (t[root].r)
t[root].mn[i]=min(t[root].mn[i],t[t[root].r].mn[i]);
}
t[root].val=max(t[root].cao,max(t[t[root].l].val,t[t[root].r].val));
}
void build(int &x,int l,int r,int fa,int dd)
{
ymh = dd;
int mid = l+r >> 1;
dd++;
if (dd==4) dd=0;
x = mid;
nth_element(t+l,t+x,t+r+1);
t[x].fa=fa;
t[x].cao=0;
for (int i=0;i<=3;i++) t[x].mn[i]=t[x].d[i];
back[t[x].num]=x;
if (l<x) build(t[x].l,l,mid-1,mid,dd);
if (x<r) build(t[x].r,mid+1,r,mid,dd);
up(x);
}
bool get(KD a,KD b)
{
for (int i=0;i<=3;i++)
if (a.d[i]>b.d[i]) return 0;
return 1;
}
int calc(int x,KD b)
{
if (!x) return 0;
for (int i=0;i<=3;i++)
if (t[x].mn[i]>b.d[i]) return 0;
return 1;
}
void query(int x,KD caonima,int dd)
{
if (!x) return ;
// cout<<x<<" "<<t[x].d[0]<<" "<<t[x].d[1]<<" "<<t[x].d[2]<<" "<<t[x].d[3]<<endl;
//cout<<"*** "<<t[x].l<<" "<<t[x].r<<endl;
int d1 = calc(t[x].l,now);
int d2 = calc(t[x].r,now);
int d=get(t[x],now);
if (d && tmp<t[x].cao) tmp=t[x].cao;
if (t[t[x].l].val>=t[t[x].r].val)
{
if (d1 && t[t[x].l].val>tmp) query(t[x].l,caonima,(dd+1)%4);
if (d2 && t[t[x].r].val>tmp) query(t[x].r,caonima,(dd+1)%4);
}
else
{
if (d2 && t[t[x].r].val>tmp) query(t[x].r,caonima,(dd+1)%4);
if (d1 && t[t[x].l].val>tmp) query(t[x].l,caonima,(dd+1)%4);
}
}
void upp(int x)
{
//t[x].val=max(t[x].val,t[x].cao);
t[x].val=max(t[x].cao,max(t[t[x].l].val,t[t[x].r].val));
}
void update(int x,int pp)
{
t[x].cao=pp;
upp(x);
x=t[x].fa;
while (x)
{
//cout<<"****"<<t[x].d[0]<<" "<<t[x].d[1]<<" "<<t[x].d[2]<<" "<<t[x].d[3]<<endl;
upp(x);
x=t[x].fa;
}
}
bool cmp(int a,int b)
{
for (int i=0;i<=3;i++)
if (t[a].d[i]!=t[b].d[i]) return t[a].d[i]<t[b].d[i];
return t[a].d[0]<t[b].d[0];
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
n=read();
for (int i=1;i<=n;i++)
{
for (int j=0;j<=3;j++) t[i].d[j]=read();
a[i]=t[i].num=i;
}
build(root,1,n,0,0);
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
tmp=0;
now = t[a[i]];
//for (int j=0;j<=3;j++) cout<<t[a[i]].d[j]<<" ";
//cout<<endl;
query(root,now,0);
ans=max(ans,tmp);
update(back[now.num],tmp+1);//cout<<tmp<<endl;
//cout<<endl;
}
cout<<t[root].val;
return 0;
}

洛谷3769[CH弱省胡策R2]TATT (KDTree)(四维LIS)的更多相关文章

  1. luoguP3769 [CH弱省胡策R2]TATT

    luoguP3769 [CH弱省胡策R2]TATT PS:做这题前先切掉 P4148简单题,对于本人这样的juruo更助于理解,当然dalao就当练练手吧 题目大意: 现在有n个四维空间中的点,请求出 ...

  2. [CH弱省胡策R2]TATT

    description 洛谷 data range \[ n\le 5\times 10^4\] solution 这就是四维偏序了... 好象时间复杂度是\(O(n^{\frac{5}{3}})\) ...

  3. [Luogu3769][CH弱省胡策R2]TATT

    luogu 题意 其实就是四维偏序. sol 第一维排序,然后就只需要写个\(3D-tree\)了. 据说\(kD-tree\)的单次查询复杂度是\(O(n^{1-\frac{1}{k}})\).所以 ...

  4. 【题解】[CH弱省胡策R2]TATT

    本蒟蒻第一道\(K-D-Tree\)维护\(dp\) Question 题目大意:求一条路径,使得其四个维度单调不降. 先排序消掉一维再说. 对于每一个点,初始的时候绝对长度是1啊.于是,先赋值一个1 ...

  5. 【弱省胡策】Round #5 Count

    [弱省胡策]Round #5 Count 太神仙了. \(DP\)做法 设\(f_{n,m,d,k}\)表示\(n*m\)的矩阵,填入第\(k\)个颜色,并且第\(k\)个颜色最少的一列上有\(d\) ...

  6. 弱省胡策 Magic

    弱省胡策 Magic 求\(n\)个点\(n\)的条边的简单联通图的个数. 毒瘤,还要写高精. 我们枚举环的大小\(k\),\(\displaystyle ans=\sum_{k=3}^nC_n^k ...

  7. 【ContestHunter】【弱省胡策】【Round0】(A)&【Round1】(B)

    DP+容斥原理or补集转化?/KD-Tree 唔……突然发现最早打的两场(打的最烂的两场)没有写记录……(太烂所以不忍记录了吗... 还是把搞出来了的两道题记录一下吧= =勉强算弥补一下缺憾…… Ro ...

  8. 【ContestHunter】【弱省胡策】【Round3】(C)

    容斥原理+Fib Orz HE的神犇们 蒟蒻只能改出来第三题……实在太弱 官方题解:http://pan.baidu.com/s/1o6MdtQq fib的神奇性质……还有解密a[i]的过程……这里就 ...

  9. 【ContestHunter】【弱省胡策】【Round2】

    官方题解:http://wyfcyx.is-programmer.com/posts/95490.html A 目前只会30分的暴力……DP好像很神的样子0.0(听说可以多次随机强行算? //Roun ...

随机推荐

  1. leaflet加载离线OSM(OpenStreetMap)

    本文为博主原创,如需转载需要署名出处. leaflet作为广为应用的开源地图操作的API,是非常受欢迎,轻量级的代码让使用者更容易操作. 废话不多说,下面直接给出范例. 首先在这个网站下载leafle ...

  2. Object-源码

    Object的结构 类构造器 一个类必须要有一个构造器的存在 , Object类源码中,是看不到构造器的,系统会自动添加一个无参构造器. Object obj = new Object(): equa ...

  3. 微前端框架single-spa初探

    前言 最近入职的一家公司采用single-spa这个微前端框架,所以自学了此框架. single-spa这个微前端框架虽然有中文文档,但是有些零散和晦涩. 所以我想在学习之余,写篇博客拉平一下这个学习 ...

  4. elsa-core:4.ASP.NET Core Server with Elsa Dashboard

    在本快速入门中,我们将了解一个最小的 ASP.NET Core 应用程序,该应用程序承载 Elsa Dashboard 组件并将其连接到 Elsa Server. ElsaDashboard + Do ...

  5. ELK学习之Logstash+Kafka篇

    上一篇介绍了一下Logstash的数据处理过程以及一些基本的配置功能,同时也提到了Logstash作为一个数据采集端,支持对接多种输入数据源,其中就包括Kafka.那么这次的学习不妨研究一下Logst ...

  6. Gitlab(1)- 简单介绍

    什么是 Gitlab 一个开源分布式版本控制系统 开发语言:Ruby 功能:管理项目源代码.版本控制.代码复用与查找.权限管控 Git 家族成员 Git:是一种版本控制系统,是一个命令,是一种工具 G ...

  7. GIT:创建、查看分支命令(git branch -vv)

    在开发过程中一般会用到Git进行版本管理,创建查看分支并与远程仓库交互是非常常见的操作. branch分支 是指在开发主线中分离出来的,做进一步开发而不影响到原来的主线. Git存储的不是一系列的更改 ...

  8. SpringApplication启动-图解

  9. Tomcat配置支持war包部署

    Tomcat配置支持war包部署 #cat /data/tomcat/conf/server.xml <?xml version='1.0' encoding='utf-8'?> < ...

  10. DP之背包经典三例

    0/1背包 HDU2602 01背包(ZeroOnePack): 有N件物品和一个容量为V的背包,每种物品均只有一件.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使价值总和最大 ...