luogu【模板】三维偏序(陌上花开)

嘟嘟嘟


很显然我开始学(CDQ)分治了。
我刚开始学的时候看了一篇博客,上面全是一些抽象的概念,看完后真是一头雾水,最后还不得不抄了这题的代码。
但这样可不行呀……
于是我就不打算再扣那篇博客,而是自己想,最后真的自己想明白了。


(个人感觉这道题跟(CDQ)分治关系不大)
首先想一下二维偏序:我们先按第一维排序,然后第二维动态的用树状数组维护。也就是说,得保证前几维都有序的前提下,才可以用数据结构维护最后一维。
现在换到了三维:所以我们必须保证在前两维有序的前提下,才能维护第三维。
首先都能想到,按第一维排序,这样就保证第一维有序了。
如何保证第二维(y)有序?如果硬排序,又会导致第一维(x)乱序。但是排序还是要排的。有没有一种排序方法能使第一维“不太乱”呢?
答案是有的:归并排序!对于区间([L, R]),虽然([L, mid])([mid + 1, R])中的(y)有序,(x)乱序,但是([mid + 1, R])中的任何一个元素的(x)仍是比([L, mid])中的任何一个都大的!因此右区间相对于左区间保证了前两维有序,所以此时我们就能用树状数组维护第三维了!
这大概就是(CDQ)分治中的左修改只会对右询问造成影响的意思吧。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e5 + 5;
const int maxm = 2e5 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, m, k;
struct Node
{
  int x, y, z, cnt, sum;
  bool operator < (const Node& oth)const
  {
    if (x != oth.x) return x < oth.x;
    if (y != oth.y) return y < oth.y;
    return z < oth.z;
  }
  bool operator != (const Node& oth)const
  {
    return x != oth.x || y != oth.y || z != oth.z;
  }
  bool operator > (const Node& oth)const
  {
    if(y != oth.y) return y < oth.y;
    return z <= oth.z;
  }
}a[maxn], t[maxn];

int c[maxm];
int lowbit(int x)
{
  return x & -x;
}
void init(int pos)
{
  for(; pos <= k; pos += lowbit(pos))
    if(c[pos] != 0) c[pos] = 0;
    else break;
}
void add(int pos, int d)
{
  for(; pos <= k; pos += lowbit(pos)) c[pos] += d;
}
int query(int pos)
{
  int ret = 0;
  for(; pos; pos -= lowbit(pos)) ret += c[pos];
  return ret;
}

void cdqSolve(int L, int R)
{
  if(L == R) return;
  int mid = (L + R) >> 1, id1 = L, id2 = mid + 1;
  cdqSolve(L, mid); cdqSolve(mid + 1, R);
  for(int i = L; i <= R; ++i)
    {
      if(id2 > R || (id1 <= mid && a[id1] > a[id2])) //这个大于号是比较第二维的,不是真正的大于号
	{
	  t[i] = a[id1++];
	  add(t[i].z, t[i].cnt);
	}
      else
	{
	  t[i] = a[id2++];
	  t[i].sum += query(t[i].z);
	}
    }
  for(int i = L; i <= R; ++i) a[i] = t[i], init(a[i].z);//分治的每一层,别忘了清空树状数组
}

int ans[maxn];

int main()
{
  m = read(); k = read();
  for(int i = 1; i <= m; ++i) t[i].x = read(), t[i].y = read(), t[i].z = read();
  sort(t + 1, t + m + 1);
  for(int i = 2, j = 1; i <= m + 1; ++i)
    if(t[j] != t[i] || i == m + 1) a[++n] = t[j], a[n].cnt = i - j, j = i;
  cdqSolve(1, n);
  for(int i = 1; i <= n; ++i) ans[a[i].sum + a[i].cnt - 1] += a[i].cnt;
  for(int i = 0; i < m; ++i) write(ans[i]), enter;
  return 0;
}
原文地址:https://www.cnblogs.com/mrclr/p/10037619.html