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

从\(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. dpkg:处理 xxx (--configure)时出错解决办法,也可用于卸载软件出错的情况

    dpkg:处理 xxx (--configure)时出错解决办法今早安装nfs时出现问题,找到该文,备份留用.然后在网上找到了这片文章,按步骤走就解决了,中间会提示自动卸载一下,执行那个命令就好了,我 ...

  2. form表单中id与name的区别

    以前经常写form表单时,不写id和name,总觉得没有什么用.后来一看后台套完的页面发现,他们都补上name,不知道所以然,就查了一下资料,吓我一跳,要是照我那样写根本不会有数据传到服务器.原来表单 ...

  3. python 截屏操作

    方法1: 用pyscreenshot,https://pypi.org/project/pyscreenshot/ 方法2:用autopy,https://pypi.org/project/autop ...

  4. mybatis第一个程序随笔

    今天继续学习了解如何写一个mybatis程序 创建了Dao层 1.1 创建一个UserDao接口 1.2 创建UserMapper.xml文件 在mybaits中文手册查找配置信息 <?xml ...

  5. ubantu上面 NFS服务器安装

    ---恢复内容开始--- N月一更............ 本博客部分参照:https://blog.csdn.net/CSDN_duomaomao/article/details/77822883  ...

  6. 遇到Web页面禁用鼠标右键操作时,该如何解禁?

    在使用Selenium做Web UI自动化测试过程中,经常需要鼠标右击Web页面检查DOM节点,用于获取Web元素的定位信息.一般情况下,绝大多数页面都是能够响应鼠标右击操作的.但出于某些目的,有些W ...

  7. JVM详解(四)——类加载过程

    类加载过程 https://www.cnblogs.com/aqsaycode/p/14885023.html

  8. 一个简单的URL访问权限校验

    前言 目前最流行的两大安全框架:SpringSecruity.Shiro 权限控制,无非就是:前端控件是否可见.是否允许请求/访问URL 本文分享一个简单的URL访问权限校验,支持/./*./**等情 ...

  9. Python - 面向对象编程 - __del__() 析构方法

    del 语句 Python 提供了 del 语句用于删除不再使用的变量 语法 del 表达式 删除变量的栗子 var = "hello" del var print(var) # ...

  10. 痞子衡嵌入式:原来i.MXRT1xxx系列里也暗藏了Product ID寄存器

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1xxx系列里暗藏的Product ID寄存器. MCU 厂商在定义一个产品系列时,通常是会预先规划产品发展路线的(即会有一 ...