概念
由于博主太懒,详细讲解见详细讲解
例题:P3810 【模板】三维偏序(陌上花开)
代码:
三维CDQ分治所白了就是一个并归排序,排序时用树状数组维护操作(前置技能点:树状数组求逆序对,感觉和权值线段树相似)
代码在详细讲解中,不再赘述。
Travel Guide
原题地址
代码:
人话翻译:给一张无向图,定义一个点的三个属性为到0、1、2三点的距离,定义q_i<q_j为三个属性都严格小于,当存在点j小于点i时,i点被覆盖。询问不会被覆盖的点的个数。
求属性用dijsktra,求个数用cdq
#include <bits/stdc++.h>
#define ll long long
#define dou double
#define pb emplace_back
#define mp make_pair
#define sot(a,b) sort(a+1,a+1+b)
#define eps 1e-8
#define int_inf 0x3f3f3f3f
#define ll_inf 0x7f7f7f7f7f7f7f7f
#define lson (curpos<<1)
#define rson (curpos<<1|1)
using namespace std;
const int maxn = 1e5 + 10;
struct Node
{
int x, y, z, id, cnt;
bool operator<(const Node &rhs)const
{
return x != rhs.x ? x < rhs.x : y != rhs.y ? y < rhs.y : z < rhs.z;
}
} info[maxn], temp[maxn];
int n, m, num = 0, sum = 0, a[maxn], dis1[maxn], dis2[maxn], dis3[maxn], vis[maxn], head[maxn];
vector<pair<int, int>>g[maxn];
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>q;
int lowbit(int x) {
return x & -x;
}
void add(int pos, int val) {
while (pos < maxn) a[pos] += val, pos += lowbit(pos);
}
int query(int pos) {
int ret = 0;
while (pos) ret += a[pos], pos -= lowbit(pos);
return ret;
}
void dijkstra(int *dis, int st) {
for (int i = 0; i <= n; ++i) dis[i] = int_inf;
dis[st] = 0;
while (q.size()) q.pop();
q.push(mp(dis[st], st));
while (q.size()) {
pair<int, int> temp = q.top(); q.pop();
if (temp.first > dis[temp.second]) continue;
for (int i = 0; i < (int)g[temp.second].size(); ++i) {
if (dis[temp.second] + g[temp.second][i].first < dis[g[temp.second][i].second]) {
dis[g[temp.second][i].second] = dis[temp.second] + g[temp.second][i].first;
q.push(mp(dis[g[temp.second][i].second], g[temp.second][i].second));
}
}
}
}
void init()
{
for (int i = 0; i < maxn; i++) vis[i] = 0;
}
void cdq(int l, int r) {
if (r - l <= 1) return;
int mid = (r + l) >> 1;
cdq(l, mid); cdq(mid, r);
int lPtr = l, rPtr = mid, cnt = 0;
while (lPtr < mid && rPtr < r) {
if (info[lPtr].y <= info[rPtr].y) {
add(info[lPtr].z, 1);
temp[cnt++] = info[lPtr++];
} else {
int curr = query(info[rPtr].z);
if (curr) vis[info[rPtr].id] = info[rPtr].cnt;
temp[cnt++] = info[rPtr++];
}
}
while (rPtr < r) {
int curr = query(info[rPtr].z);
if (curr) vis[info[rPtr].id] = info[rPtr].cnt;
temp[cnt++] = info[rPtr++];
}
for (int i = l; i < lPtr; ++i) add(info[i].z, -1);
while (lPtr < mid) temp[cnt++] = info[lPtr++];
for (int i = 0; i < cnt; ++i) info[i + l] = temp[i];
}
int main()
{
scanf("%d%d", &n, &m); sum = n, num = 0;
while (m--) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].pb(mp(w, v)); g[v].pb(mp(w, u));
}
// maintain distance
dijkstra(dis1, 0); dijkstra(dis2, 1); dijkstra(dis3, 2);
for (int i = 0; i < n; ++i) info[i].x = dis1[i], info[i].y = dis2[i], info[i].z = dis3[i], head[++num] = info[i].z;
sort(head + 1, head + 1 + num);
// 离散化处理
num = unique(head + 1, head + 1 + num) - head - 1;
for (int i = 0; i < n; ++i) info[i].z = lower_bound(head + 1, head + 1 + num, info[i].z) - head;
sort(info, info + n);
int p = 1, tot = 0;
// spj equal
for (int i = 0; i < n; ++i) {
if (info[i].x == info[i + 1].x && info[i].y == info[i + 1].y && info[i].z == info[i + 1].z) {
p++; continue;
}
info[i].cnt = p; info[tot++] = info[i]; p = 1;
}
n = tot;
for (int i = 0; i < n; ++i) info[i].id = i;
cdq(0, n);
for (int i = 0; i < n; ++i) sum -= vis[i];
printf("%d\n", sum);
}
总体思路就是把被覆盖的点标记出来。
求最长上升序列的题参考博客
中序遍历,然后dp
讨论:dilworth定理
参考博客
博文里的最少全序集是否可以理解为:最长全序集的个数?
举例:1 2 3 2 3
定义二元关系<=
则最少全序集个数为2(1,2,3),(2,3)
(解释为啥只有两个:每次都要有新元素,最多用两个?参考导弹拦截)
最长反链长度为2(3,2)
参考cf round 617 E2
qsc