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

从\(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. 媒体查询media query

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. Linux下修改mysql默认最大连接数

    liunx下修改mysql最大连接数(Centos下测试通过)1.查看当前系统下mysql设置的最大连接数方式一.mysqladmin -uroot -p variables |grep max_co ...

  3. Python面试题小试牛刀

    1.声明变量注意事项有那些? 1.变量名只能用字母.数字或下划线的任意组合 2.变量名不能以数字为第一个字符 3.内置函数名不能用 2.如何查看变量在内存中的地址? id(变量名) 3.写代码 a. ...

  4. Junit5快速入门指南-3

    Fixtures 是测试中非常重要的一部分.他们的主要目的是建立一个固定/已知的环境状态以确保 测试可重复并且按照预期的方式运行.比如在app测试中 基类@BeforeClass 配置初始化,初始化d ...

  5. Apache Hudi内核之文件标记机制深入解析

    1. 摘要 Hudi 支持在写入时自动清理未成功提交的数据.Apache Hudi 在写入时引入标记机制来有效跟踪写入存储的数据文件. 在本博客中,我们将深入探讨现有直接标记文件机制的设计,并解释了其 ...

  6. Configuration对象和SessionFactory会话池

    一.加载核心配置文件方式 二.加载映射文件方式 三.SessionFactory相当于连接池 四.获取session会话 同一个线程中获取的session两种方法获取的是同一个session对象: 不 ...

  7. 使用easyui进行上左右布局

    在后台管理系统开发的过程中,上左右的布局是最常见的页面布局方式,现在我们来看看使用easyui这个jquery前端框架如何快速搭建一个可用的页面框架. 1.在页面中引入easyui所需的文件 1 &l ...

  8. 这些解决 Bug 的套路,你都会了不?

    最近整理了我原创的 140 篇编程经验和技术文章,欢迎大家阅读,一起成长!指路:https://t.1yb.co/ARnD 大家好,我是鱼皮. 学编程的过程中,我们会遇到各式各样的 Bug,也常常因为 ...

  9. xxl-job <=2.0.2 反序列化漏洞

    xxl-job <=2.0.2 反序列化漏洞 搭建 https://github.com/xuxueli/xxl-job/releases/tag/2.0.2 下载源码,导入idea,mysql ...

  10. CVPR顶会论文爬取存入MySQL数据库(标题、摘要、作者、PDF链接和原地址)

    main.py import pymysql import re import requests # 连接数据库函数 from bs4 import BeautifulSoup def insertC ...