洛谷3769[CH弱省胡策R2]TATT (KDTree)(四维LIS)
真是一个自闭的题目(调了一个上午+大半个下午)
从\(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)的更多相关文章
- luoguP3769 [CH弱省胡策R2]TATT
luoguP3769 [CH弱省胡策R2]TATT PS:做这题前先切掉 P4148简单题,对于本人这样的juruo更助于理解,当然dalao就当练练手吧 题目大意: 现在有n个四维空间中的点,请求出 ...
- [CH弱省胡策R2]TATT
description 洛谷 data range \[ n\le 5\times 10^4\] solution 这就是四维偏序了... 好象时间复杂度是\(O(n^{\frac{5}{3}})\) ...
- [Luogu3769][CH弱省胡策R2]TATT
luogu 题意 其实就是四维偏序. sol 第一维排序,然后就只需要写个\(3D-tree\)了. 据说\(kD-tree\)的单次查询复杂度是\(O(n^{1-\frac{1}{k}})\).所以 ...
- 【题解】[CH弱省胡策R2]TATT
本蒟蒻第一道\(K-D-Tree\)维护\(dp\) Question 题目大意:求一条路径,使得其四个维度单调不降. 先排序消掉一维再说. 对于每一个点,初始的时候绝对长度是1啊.于是,先赋值一个1 ...
- 【弱省胡策】Round #5 Count
[弱省胡策]Round #5 Count 太神仙了. \(DP\)做法 设\(f_{n,m,d,k}\)表示\(n*m\)的矩阵,填入第\(k\)个颜色,并且第\(k\)个颜色最少的一列上有\(d\) ...
- 弱省胡策 Magic
弱省胡策 Magic 求\(n\)个点\(n\)的条边的简单联通图的个数. 毒瘤,还要写高精. 我们枚举环的大小\(k\),\(\displaystyle ans=\sum_{k=3}^nC_n^k ...
- 【ContestHunter】【弱省胡策】【Round0】(A)&【Round1】(B)
DP+容斥原理or补集转化?/KD-Tree 唔……突然发现最早打的两场(打的最烂的两场)没有写记录……(太烂所以不忍记录了吗... 还是把搞出来了的两道题记录一下吧= =勉强算弥补一下缺憾…… Ro ...
- 【ContestHunter】【弱省胡策】【Round3】(C)
容斥原理+Fib Orz HE的神犇们 蒟蒻只能改出来第三题……实在太弱 官方题解:http://pan.baidu.com/s/1o6MdtQq fib的神奇性质……还有解密a[i]的过程……这里就 ...
- 【ContestHunter】【弱省胡策】【Round2】
官方题解:http://wyfcyx.is-programmer.com/posts/95490.html A 目前只会30分的暴力……DP好像很神的样子0.0(听说可以多次随机强行算? //Roun ...
随机推荐
- 使用栅格系统开发响应式页面——logo+nav实例
小屏时: 中屏及以上时: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- VS2017 创建并测试 C++ dll
生成DLL 创建工程: Create new project -> 选择Visual C++ -> Windows Desktop -> Dynamic-Link Library ( ...
- Mysql主从复制、半同步复制、并行复制
MySQL之间数据复制的基础是二进制日志文件(binary log file).一台MySQL数据库一旦启用二进制日志后,其作为master,它的数据库中所有操作都会以"事件"的方 ...
- Java数八大据类型的拓展
public class 数据类型拓展问题 { public static void main(String[] args) { //================================= ...
- EFCore 开始
1. 数据准备 新建类库项目--实体 NuGet安装: Microsoft.EntityFrameworkCore 新建类库项目--DbContext NuGet安装: Microsoft.Entit ...
- 聚类算法与K-means实现
聚类算法与K-means实现 一.聚类算法的数学描述: 区别于监督学习的算法(回归,分类,预测等),无监督学习就是指训练样本的 label 未知,只能通过对无标记的训练样本的学习来揭示数据的内在规律和 ...
- etcd学习(9)-etcd中的存储实现
etcd中的存储实现 前言 V3和V2版本的对比 MVCC treeIndex 原理 MVCC 更新 key MVCC 查询 key MVCC 删除 key 压缩 周期性压缩 版本号压缩 boltdb ...
- Python习题集(十五)
每天一习题,提升Python不是问题!!有更简洁的写法请评论告知我! https://www.cnblogs.com/poloyy/category/1676599.html 题目 请写一个函数,该函 ...
- Appium问题解决方案(8)- selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: Could not sign with default certificate.
背景 运行时代码报错: selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occ ...
- 源码解读Dubbo分层设计思想
一.Dubbo分层整体设计概述 我们先从下图开始简单介绍Dubbo分层设计概念: (引用自Duboo开发指南-框架设计文档) 如图描述Dubbo实现的RPC整体分10层:service.config. ...