题意:  

  三维坐标,对于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. SpinLock(自旋锁)

    SpinLock(自旋锁) SpinLock 结构是一个低级别的互斥同步基元,它在等待获取锁时进行旋转. 在多核计算机上,当等待时间预计较短且极少出现争用情况时,SpinLock 的性能将高于其他类型 ...

  2. Ado.net对象

    Connection对象主要提供与数据库的连接功能 Command 对象用于返回数据,修改数据,运行存储过程以及发送或检索参数信息的数据库命令. DataReader对象通过Command对象提供从数 ...

  3. Oracle start with connect by prior 用法

    Oracle start with connect by prior 用法    语法: select * from 表名 where 条件1 start with 条件2 connect by pr ...

  4. 将实体转成XML,XML节点顺序由我控制

    一.前言 由于有时候返回xml格式比较严格,需要按照一定的顺序排列节点才能够符合要求,这里主要用到了自定义一个List<string> 字符顺序,再让实体属性按照List定义好的顺序重新排 ...

  5. 用C++写出hanoi

    汉诺塔(港台:河內塔)是根据一个传说形成的數學问题有三根杆子A,B,C.A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小.要求按下列规则将所有圆盘移至C杆:-每次只能移动一个圆盘-大的盘 ...

  6. C语言中的结构体和C++中的结构体以及C++中类的区别

    c++中结构体可以定义一个函数 C中的结构体和C++中结构体的不同之处:在C中的结构体只能自定义数据类型,结构体中不允许有函数,而C++中的结构体可以加入成员函数. C++中的结构体和类的异同: 一. ...

  7. Emacs显示行号

    在配置.emacs文件中加上    (global-linum-mode t) 启动emacs后按     m-x  global-linum-mode 就可以显示行号,但是每次打开emacs,要重新 ...

  8. 【JAVA编码专题】 JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  9. 制作 leanote docker 镜像

    leanote 使用 mongodb 存储数据,如果把 mongodb 单独做成一个镜像,初始化数据时比较麻烦,所以最后还是决定把 mongodb 和 leanote 放到同一个镜像里边. docke ...

  10. $(function(){})与(function($){....})(jQuery)的区别

    $(function(){}); 全写为 $(docunemt).ready(function(){ }); 意义为在DOM加载完毕后执行ready()方法 (function($){....})(j ...