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

从\(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. 使用spring向service里面注入dao不成功。

    因为原来的程序没有使用spring.后来加spring的时候action有个地方的new没有改!!! new了个新的实现层 不是spring管理的对象.

  2. C#·好文分享

    时间:2018-11-14 记录:byzqy 好文收藏,集中分享! 标题:C#接口<通俗解释> 地址:https://www.cnblogs.com/hamburger/p/4681681 ...

  3. (二)羽夏看C语言——容器

    写在前面   由于此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇 ...

  4. Mybatis-plus<二>通用CRUD,分页

    Mybatis-plus<二>通用CRUD,分页 与博客Mybatis-plus<一>为同一个Springboot项目. Demo GitHub下载地址:https://git ...

  5. python库--flashtext--大规模数据清洗利器

    flashtext.keyword (flashtext) 类/方法 返回值 参数 说明 .KeywordProcessor() 对象kp case_sensitive=False 是否区分大小写 添 ...

  6. C# 获取应用程序内存

    double usedMemory = 0;             Process p = Process.GetProcesses().Where(x => x.ProcessName.Co ...

  7. sonar扫描java项目报错

    安装maven 配置path 验证maven,看到以下信息证明已经成功 扫描项目 扫描以下项目: kf-buss-nhgip-smartoffice-business-thirdparty 项目的配置 ...

  8. chrome 的 options 参数

    在使用selenium浏览器渲染技术,爬取网站信息时,默认情况下就是一个普通的纯净的chrome浏览器,而我们平时在使用浏览器时,经常就添加一些插件,扩展,代理之类的应用.相对应的,当我们用chrom ...

  9. 前端js单元测试 使用mocha、chai、sinon,karma

    karma(因果报应)  提供在浏览器上测试  可以同时跑在多个浏览器下 mocha测试框架  其他测试框架还有Jasmine chai断言库  expect = chai.expect sinon ...

  10. flask_sqlalchemy 查询结果转dict 终极解决方案

    之前为了学习Python,试着拿Flask作框架搞小网站,感觉还不错,基本就抛弃了PHP.前段时间做了一个微信小程序,想着yii框架拿来写几十个小接口是不是浪费了,就继续用flask写api了,哪想到 ...