1. 程式人生 > >luogu【模板】三維偏序(陌上花開)

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;
}