题意:  

  三维坐标,对于1个点,找出有多少个点,3个坐标都比该点小!

Sample Input
1
4
10 4 7
10 6 6
8 2 5
7 3 10
 
Sample Output
1
1
0
0
 
 
首先是方法一:
  很常见的三维偏序做法,先将所有输入点按要求排序,然后向数据结构插入的时候就可以确定先插入的x值一定比后插入的x小,这样就将三维转化为2维。
  2维的话就用树状数组套线段树。根据y建树状数组,根据z建线段树,每一个树状数组的点都对应着一个线段树。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
typedef long long LL;
const int maxn = 2e5 + ;
const int low(int x){ return x&-x; }
int T, n;
struct point
{
int x, y, z, id, cnt;
void read(){ scanf("%d%d%d", &x, &y, &z); cnt = ; }
bool operator<(const point& a) const
{
if (x == a.x&&y == a.y) return z < a.z;
if (x == a.x) return y < a.y;
return x < a.x;
}
}a[maxn]; bool cmp(const point&a, const point&b)
{
return a.id < b.id;
} struct Tree
{
//将y坐标用树状数组维护,z坐标用线段树维护。然后在树状数组中的每一个点都对应着一个线段树。
//查询小于(y,z)的所有点只需要在1-y这个树状数组中的所有点对应的线段树中都查询<z的个数
int tot, first[maxn], f[maxn * ], L[maxn * ], R[maxn * ], lit, lim;
//lim代表最大的z,lit代表最大的y
void clear(int x, int y)
{
lit = x; lim = y;
for (int i = ; i <= lit; i++)
{
first[i] = i;
f[i] = R[i] = L[i] = ;
}
tot = lit + ;
}
int newnode()
{
f[tot] = R[tot] = L[tot] = ;
return tot++;
}
void ins(int x, int l, int r, int v)
{
f[x] += ;
if (l == r) return;
int mid = l + r >> ;
if (v <= mid) { if (!L[x]) L[x] = newnode(); ins(L[x], l, mid, v); }
else { if (!R[x]) R[x] = newnode(); ins(R[x], mid + , r, v); }
}
void insert(int x, int y)
{
for (int i = x; i <= lit; i += low(i)) ins(first[i], , lim, y);
}
int find(int x, int l, int r, int v)//对于第x棵线段树,查询有多少权值<v的
{
if (r <= v) return f[x];
int mid = l + r >> , ans = ;
if (v <= mid) ans = find(L[x], l, mid, v);
else ans = f[L[x]] + find(R[x], mid + , r, v);
return ans;
}
int get(int x, int y)//通过树状数组统计1-x这个范围内的线段树中<y的点的总数
{
int res = ;
for (int i = x; i; i -= low(i)) res += find(first[i], , lim, y);
return res;
}
}tree; int main()
{
scanf("%d", &T);
while (scanf("%d", &n) != EOF, T--)
{
for (int i = ; i < n; i++) a[i].read(), a[i].id = i;
sort(a, a + n);//将输入的点按照题意排序
// for(int i=0;i<n;i++){
// cout<<a[i].x<<endl;
// }
for (int i = n - ; i >= ; i--)
if (a[i].x == a[i + ].x&&a[i].y == a[i + ].y&&a[i].z == a[i + ].z) a[i].cnt = a[i + ].cnt + ; int ans1 = , ans2 = ;
for (int i = ; i < n; i++) ans1 = max(ans1, a[i].y), ans2 = max(ans2, a[i].z);
tree.clear(ans1, ans2);
for (int i = ; i < n; i++)//将三维转为2维,以为已经排好序,所以先插入的点的x坐标<后插入点的x坐标,接下来只需要比较y和z
{
a[i].cnt += tree.get(a[i].y, a[i].z);
tree.insert(a[i].y, a[i].z);
}
sort(a, a + n, cmp);
for (int i = ; i < n; i++) printf("%d\n", a[i].cnt);
}
return ;
}

方法二:cdq分治+树状数组(从别人那学到的)

  首先先拿二维的点解释一下cdq分治,对于x坐标有序的点,点i可以对点j产生贡献,当且仅当yi<=yj。 
  我们考虑先把x坐标排序,那么对于某个区间[l,r],其x坐标必然有序。 
  我们再对[l,mid]和[mid + 1,r]进行y坐标的重新排序。 
  然后考虑计算[l,mid]之间所有点对于[mid + 1,r]的区间的贡献。
  我们考虑是先递归下去,然后排序比较对还是先排序计算贡献,然后递归下去比较对。 
  嗯……显然,先递归下去之后,会产生两个y有序的区间[l,mid],[mid + 1,r]。 
  并且我们知道对于左区间的所有点的x值是小于右区间的。 
  然后我们就可以直接计算左区间对于右区间的贡献。 
  考虑合并两个区间,显然归并排序一下就可以做到O(n)。
  然后我们就可以继续回溯上去。 
  如果先计算贡献,那么需要把左右区间对y进行排序,然后计算完左边对右边的贡献之后,再按照x坐标排回去。<常数大了很多2333>

  三维的话把左区间的所有z坐标都插入到树状数组里。然后对于右边的区间每次询问z坐标即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define u t[x]
using namespace std;
const int N = ;
int n,ans[N],t[N];
struct Point {int x,y,z,id,sum;}p[N];
bool cx(Point a,Point b){return a.x == b.x ? (a.y == b.y ? a.z < b.z : a.y < b.y) : a.x < b.x;}
bool cy(Point a,Point b)
{
if(a.y != b.y)return a.y < b.y;
return a.x == b.x ? a.z < b.z : a.x < b.x;
}
void add(int x,int val){for(;x <= ;x += x & -x)u += val;}
int Qry(int x){int s = ;for(;x;x -= x & -x)s += u;return s;}
void solve(int l,int r)
{
if(l == r)return;
int mid = l + r >> ;
solve(l,mid),solve(mid + ,r);
sort(p + l,p + mid + ,cy);
sort(p + mid + ,p + r + ,cy);
int j = l;
for(int i = mid + ;i <= r;i ++)
{
for(;j <= mid && p[j].y <= p[i].y;j ++)
add(p[j].z,);
p[i].sum += Qry(p[i].z);
}
for(j --;j >= l;j --)add(p[j].z,-);
}
int main ()
{
int _;scanf("%d",&_);
while(_--)
{
scanf("%d",&n);
Rep(i,n)scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z),p[i].id = i,p[i].sum = ;
sort(p + ,p + + n,cx);
solve(,n);
for(int i = ;i <= n;)
{
int j = i + ;
int tmp = p[i].sum;
for(;j <= n && p[i].x == p[j].x && p[i].y == p[j].y && p[i].z == p[j].z ;++ j)tmp = max(tmp,p[j].sum);
for(int k = i;k < j;k ++)ans[p[k].id] = tmp;
i = j;
}
Rep(i,n)
printf("%d\n",ans[i]);
}
return ;
}

HDU 5618 Jam's problem again的更多相关文章

  1. cdq分治(hdu 5618 Jam's problem again[陌上花开]、CQOI 2011 动态逆序对、hdu 4742 Pinball Game、hdu 4456 Crowd、[HEOI2016/TJOI2016]序列、[NOI2007]货币兑换 )

    hdu 5618 Jam's problem again #include <bits/stdc++.h> #define MAXN 100010 using namespace std; ...

  2. HDU 5618 Jam's problem again(三维偏序,CDQ分治,树状数组,线段树)

    Jam's problem again Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Othe ...

  3. HDU 5618 Jam's problem again CDQ分治 BC ROUND 70

    题意:给你1e5个点(x,y,z),对于每一个点询问有多少个点(x1,y1,z1)满足x1<=x&&y1<=y&&z1<=z 分析:(官方题解奉上)很 ...

  4. HDU 5618 Jam's problem again (cdq分治+BIT 或 树状数组套Treap)

    题意:给n个点,求每一个点的满足 x y z 都小于等于它的其他点的个数. 析:三维的,第一维直接排序就好按下标来,第二维按值来,第三维用数状数组维即可. 代码如下: cdq 分治: #pragma ...

  5. HDU5618 Jam's problem again CDQ分治

    Jam's problem again CDQ分治 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意: \[ 有n 个元素,第 i 个元素有 ...

  6. HDU 5616 Jam's balance(Jam的天平)

    HDU 5616 Jam's balance(Jam的天平) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K ...

  7. HDU 5832 A water problem(某水题)

    p.MsoNormal { margin: 0pt; margin-bottom: .0001pt; text-align: justify; font-family: Calibri; font-s ...

  8. hdu 1402 A * B Problem Plus FFT

    /* hdu 1402 A * B Problem Plus FFT 这是我的第二道FFT的题 第一题是完全照着别人的代码敲出来的,也不明白是什么意思 这个代码是在前一题的基础上改的 做完这个题,我才 ...

  9. HDU 4291 A Short problem(2012 ACM/ICPC Asia Regional Chengdu Online)

    HDU 4291 A Short problem(2012 ACM/ICPC Asia Regional Chengdu Online) 题目链接http://acm.hdu.edu.cn/showp ...

随机推荐

  1. .NET开发人员必须知道的八个网站

    对于不熟悉.NET技术的朋友,需要说明一下,.NET提供了一个平台和一些相应的工具,.NET开发人员可以使用它们来在开发Windows桌面,互联网,甚至是手持移动设备上构建极富交互性的应用.很有可能你 ...

  2. .NET程序猿 - 提升幸福感的组件一览

    1.Newtonsoft.Json.net 操作JSON最简便的方式.  .Net 3.5开始,Framework集成Json序列化器:JavaScriptSerializer,然而Json.net给 ...

  3. aspx生成验证码

    //定义方法 public partial class VerificationCode : System.Web.UI.Page    {        string ImagePath = &qu ...

  4. vb将窗体中的控件或某种颜色透明

    Option Explicit ' ******************** 窗体透明 ******************** '***Module.bas '**** Public Declare ...

  5. call()与apply()传参需要注意的一点

    call()与apply()是用来改变函数体内的this指向,第一个参数是调用函数的母对象,他是调用上下文,函数体内通过this来获得对它的引用,换句话说就是第一参数===函数中的this. 但是如下 ...

  6. 8 Hbase get方式获取数据

    package com.hikvision.hbase.vertify.test; import org.apache.hadoop.conf.Configuration; import org.ap ...

  7. java必备基础知识(一)

    学习的一点建议: 每一门语言的学习都要从基础知识开始,学习是一个过程,"万丈高楼平地起",没有一个好的地基,想必再豪华的高楼大厦终究有一天会倒塌.因此,我们学习知识也要打牢根基,厚 ...

  8. JAVA GUI 工具

    Java GUI图形界面开发工具   上大学那会儿比较主流的Java图形开发插件是:Visual Editor 和 SWT Designer, 不久又出了个Jigloo, 但去官网看了下发现这个东西也 ...

  9. 基于cygwin构建u-boot(四)libgcc引用

    接上文,config.mk文件修改后,即使没有.depend也可以正常处理了: 六.错误:gcclib引用错误 完成之前几篇的工作后,程序就可以一直执行了,直到最后生成u-boot, 出现如下错误告警 ...

  10. MVC 学习随笔(一)

    Model的绑定. (一)使用NameValueCollectionValueProvider C# 对NameValueCollectionValueProvider的支持是通过下面的类实现的 // ...