\(n\) 维偏序即给出若干个点对 \((a_{i},b_{i},\cdots,n_{i})\),对每个 \(i\) 求出满足 \(a_{j}\gt a_{i},b_{j}\gt b_{i}\cdots,n_{j}\gt n_{i}\) 的 \(j\) 的个数

一维偏序

直接用权值线段树或者树状数组. 或者你直接离散化开桶前缀和.

二维偏序

考虑到先对全部点对按 \(a_{i}\) 排序,这样,任何点对只能被它前面的点产生贡献.

a 1 1 1 2 3 4 4 5
b 1 2 3 3 4 2 4 1

可以发现,\(i\) 处的答案可以转化为:其前方的点中满足一维偏序的点数.

所以我们考虑对点对从左到右扫一遍,每次向维护的动态树状数组中加入一个新节点,统计前缀和即可.

三维偏序

CDQ+数据结构 解法

按二维偏序的套路,先对 \(a_{i}\) 排序

然后我们考虑这样分解问题:向下对 \(b\) 进行归并排序,每次只统计 \(i\in[l,mid],j\in[mid+1,r]\) 的答案. 可以发现,对于区间内部的答案,总会有递归下去的子区间来求解,对于区间之间的答案,当前这一步将会求解出来,因此我们恰好统计了所有答案,这就是 CDQ 分治.

为什么我们在进行统计答案的时候需要对 \(b\) 排序?假设现在两个子区间内的 \(b\) 都是有序的(\(a\) 是完全有序的,我们不管它),那么在对 \(b\) 归并排序的过程中,会发现,只有 \(i\) 能对 \(j\) 产生贡献(因为 \(a\) 数组的限制),并且只有 \(i\) 数对的 \(b\) 比 \(j\) 数对的 \(b\) 更小的时候,\(i\) 才可能对 \(j\) 有贡献,表现在归并排序里就是只有比 \(j\) 先选的 \(i\) 才能对 \(j\) 有贡献. 基于这一点,我们才能用树状数组+CDQ分治来解决偏序问题.

#include<bits/stdc++.h>
using namespace std;
#define p &
int n,k;
struct node{
int a,b,c;
int cnt,res;
bool operator !=(const node &A)const{
return a!=A.a or b!=A.b or c!=A.c;
}
};
node e[100001];
node ue[100001];
int m,t;
int res[100001];
struct bit{
int a[200001];
function<int(int)> lowbit=[](int x){return x&-x;};
inline void add(int pos,int val){
while(pos<=k){
a[pos]+=val;
pos+=lowbit(pos);
}
};
inline int ask(int pos){//sum[pos]
int res=0;
while(pos){
res+=a[pos];
pos-=lowbit(pos);
}
return res;
};
}BIT;
bool cmp1(node a,node b){
if(a.a!=b.a) return a.a<b.a;
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
}
bool cmp2(node a,node b){
if(a.b!=b.b) return a.b<b.b;
return a.c<b.c;
}
void cdq(int l,int r){
if(l==r) return;//i!=j 单个区间无法产生贡献
int mid=(l+r)/2;
cdq(l,mid);//递归求解子区间
cdq(mid+1,r);
sort(ue+l,ue+mid+1,cmp2);//按 b 排序,方便归并
sort(ue+mid+1,ue+r+1,cmp2);
int i=l,j=mid+1;//归并排序
//注意这里并不会真的排序,因为父区间会有 sort 的
while(j<=r){
while(i<=mid and ue[i].b<=ue[j].b){
//当前 i 更小,将会对 j 与 j 后方的位置产生贡献
BIT.add(ue[i].c,ue[i].cnt);
i++;
}
//后面的 i 值不再能够产生贡献了,此时暂时统计答案
ue[j].res+=BIT.ask(ue[j].c);
j++;
}
for(int k=l;k<=i-1;++k){
BIT.add(ue[k].c,-ue[k].cnt);
//清空 BIT,memset 也行就是有点慢
}
}
int main(){
scanf("%d %d",p n,p k);
for(int i=1;i<=n;++i){
scanf("%d %d %d",p e[i].a,p e[i].b,p e[i].c);
}
sort(e+1,e+n+1,cmp1);
for(int i=1;i<=n;++i){
t++;
if(e[i]!=e[i+1]){
//去重,防止相同的元素统计不到而漏答案
ue[++m]={e[i].a,e[i].b,e[i].c,t};
t=0;
}
}
cdq(1,m);
for(int i=1;i<=m;++i){
//去重在这里用到了
res[ue[i].res+ue[i].cnt-1]+=ue[i].cnt;
}
for(int i=0;i<=n-1;++i){
printf("%d\n",res[i]);
}
}

数据结构+数据结构

众所周知,三维偏序可以用树套树来解决.

我并不是非常喜欢写树套树,因此就写了一个树套树

我说怎么没人线段树套线段树,原来是会 TLE 啊,哈哈哈

考虑到这个题排完序只需要对每一维维护动态前缀和即可,可以想到树套树.

UPD:糙拟妈啥比树套树爱谁调谁调

10pts

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct node{
int val;
node *tol,*tor;
node(){val=0;tol=tor=NULL;}
};
class Stree{
public:
node *root;
void change(node *&now,int pos,int val,int l,int r){
if(!now) now=new node();
if(l==r){
now->val+=val;
return;
}
int mid=(l+r)/2;
if(pos<=mid) change(now->tol,pos,val,l,mid);
else change(now->tor,pos,val,mid+1,r);
now->val=(now->tol?now->tol->val:0)+(now->tor?now->tor->val:0);
}
int ask(node *now,int l,int r,int nl,int nr){
if(!now) return 0;
if(l==nl and r==nr) return now->val;
int mid=(nl+nr)/2;
if(r<=mid) return ask(now->tol,l,r,nl,mid);
else if(l>mid) return ask(now->tor,l,r,mid+1,nr);
return ask(now->tol,l,mid,nl,mid)|ask(now->tor,mid+1,r,mid+1,nr);
}
};
//struct node{
// int val;
// int tol,tor;
//};
//vector<node>t;
//int newnode(){
// t.push_back({0,0,0});
// return t.size()-1;
//}
//class Stree{
// public:
// int root=0;
// void change(int &now,int pos,int val,int l,int r){
// if(!now) now=newnode();
// if(l==r){
// t[now].val+=val;
// return;
// }
// int mid=(l+r)/2;
// if(pos<=mid) change(t[now].tol,pos,val,l,mid);
// else change(t[now].tor,pos,val,mid+1,r);
// t[now].val=t[t[now].tol].val+t[t[now].tor].val;
// }
// int ask(int now,int l,int r,int nl,int nr){
// if(!now) return 0;
// if(l==nl and r==nr) return t[now].val;
// int mid=(nl+nr)/2;
// if(r<=mid) return ask(t[now].tol,l,r,nl,mid);
// else if(l>mid) return ask(t[now].tor,l,r,mid+1,nr);
// return ask(t[now].tol,l,mid,nl,mid)+ask(t[now].tor,mid+1,r,mid+1,nr);
// }
//};
class BIT{
public:
Stree a[200001];
inline int lowbit(int x){return x&-x;}
inline void add(int posx,int posy,int val){
for(int i=posx;i<=k;i+=lowbit(i)){
a[i].change(a[i].root,posy,val,1,k);
}
}
inline int ask(int x,int y){
int ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=a[i].ask(a[i].root,1,y,1,k);
}
return ans;
}
}bit;
struct un{
int x,y,z;
bool operator <(const un &A)const{
if(x!=A.x) return x<A.x;
if(y!=A.y) return y<A.y;
return z<A.z;
}
bool operator ==(const un &A)const{
return x==A.x and y==A.y and z==A.z;
}
}d[100001];
int ans[100001];
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%d %d %d",&d[i].x,&d[i].y,&d[i].z);
}
sort(d+1,d+n+1);
int t=1;
for(int i=1;i<=n;++i){
if(d[i+1]==d[i]){
t++;
continue;
}
bit.add(d[i].y,d[i].z,t);
int res=bit.ask(d[i].y,d[i].z);
ans[res]+=t;
t=1;
}
for(int i=1;i<=n;++i){
printf("%d\n",ans[i]);
}
}

UPD: 码的调出来了. 哪个啥比把 | 设计在 + 旁边了

ask() 函数最后一行把 + 写成 |

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct node{
int val;
node *tol,*tor;
node(){val=0;tol=tor=NULL;}
};
class Stree{
public:
node *root;
void change(node *&now,int pos,int val,int l,int r){
if(!now) now=new node();
if(l==r){
now->val+=val;
return;
}
int mid=(l+r)/2;
if(pos<=mid) change(now->tol,pos,val,l,mid);
else change(now->tor,pos,val,mid+1,r);
now->val=(now->tol?now->tol->val:0)+(now->tor?now->tor->val:0);
}
int ask(node *now,int l,int r,int nl,int nr){
if(!now) return 0;
if(l==nl and r==nr) return now->val;
int mid=(nl+nr)/2;
if(r<=mid) return ask(now->tol,l,r,nl,mid);
else if(l>mid) return ask(now->tor,l,r,mid+1,nr);
return ask(now->tol,l,mid,nl,mid)+ask(now->tor,mid+1,r,mid+1,nr);
}
};
//struct node{
// int val;
// int tol,tor;
//};
//vector<node>t;
//int newnode(){
// t.push_back({0,0,0});
// return t.size()-1;
//}
//class Stree{
// public:
// int root=0;
// void change(int &now,int pos,int val,int l,int r){
// if(!now) now=newnode();
// if(l==r){
// t[now].val+=val;
// return;
// }
// int mid=(l+r)/2;
// if(pos<=mid) change(t[now].tol,pos,val,l,mid);
// else change(t[now].tor,pos,val,mid+1,r);
// t[now].val=t[t[now].tol].val+t[t[now].tor].val;
// }
// int ask(int now,int l,int r,int nl,int nr){
// if(!now) return 0;
// if(l==nl and r==nr) return t[now].val;
// int mid=(nl+nr)/2;
// if(r<=mid) return ask(t[now].tol,l,r,nl,mid);
// else if(l>mid) return ask(t[now].tor,l,r,mid+1,nr);
// return ask(t[now].tol,l,mid,nl,mid)+ask(t[now].tor,mid+1,r,mid+1,nr);
// }
//};
class BIT{
public:
Stree a[200001];
inline int lowbit(int x){return x&-x;}
inline void add(int posx,int posy,int val){
for(int i=posx;i<=k;i+=lowbit(i)){
a[i].change(a[i].root,posy,val,1,k);
}
}
inline int ask(int x,int y){
int ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=a[i].ask(a[i].root,1,y,1,k);
}
return ans;
}
}bit;
struct un{
int x,y,z;
bool operator <(const un &A)const{
if(x!=A.x) return x<A.x;
if(y!=A.y) return y<A.y;
return z<A.z;
}
bool operator ==(const un &A)const{
return x==A.x and y==A.y and z==A.z;
}
}d[200001];
int ans[200001];
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%d %d %d",&d[i].x,&d[i].y,&d[i].z);
}
sort(d+1,d+n+1);
int t=1;
for(int i=1;i<=n;++i){
if(d[i+1]==d[i]){
t++;
continue;
}
bit.add(d[i].y,d[i].z,t);
int res=bit.ask(d[i].y,d[i].z);
ans[res]+=t;
t=1;
}
for(int i=1;i<=n;++i){
printf("%d\n",ans[i]);
}
}

[OI] 偏序的更多相关文章

  1. 牛客OI周赛9-提高组题目记录

    牛客OI周赛9-提高组题目记录 昨天晚上做了这一套比赛,觉得题目质量挺高,而且有一些非常有趣而且非常清奇的脑回路在里边,于是记录在此. T1: 扫雷 题目链接 设 \(f_i\) 表示扫到第 \(i\ ...

  2. [bzoj] 3263 陌上花开 洛谷 P3810 三维偏序|| CDQ分治 && CDQ分治讲解

    原题 定义一个点比另一个点大为当且仅当这个点的三个值分别大于等于另一个点的三个值.每比一个点大就为加一等级,求每个等级的点的数量. 显然的三维偏序问题,CDQ的板子题. CDQ分治: CDQ分治是一种 ...

  3. 正睿OI集训游记

    什么嘛....就是去被虐的... 反正就是难受就是了.各种神仙知识点,神仙题目,各式各样的仙人掌..... 但是还是学会了不少东西...... 应该是OI生涯最后一次集训了吧.... 这次的感言还是好 ...

  4. 洛谷 P2163 [SHOI2007]园丁的烦恼 (离线sort,树状数组,解决三维偏序问题)

    P2163 [SHOI2007]园丁的烦恼 题目描述 很久很久以前,在遥远的大陆上有一个美丽的国家.统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草. 有一天国王漫步在花园 ...

  5. cdq分治(偏序)

    偏序问题: https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen 优质题 ...

  6. 【教程】CDQ套CDQ——四维偏序问题

    前言 上一篇文章已经介绍了简单的CDQ分治,包括经典的二维偏序和三维偏序问题,还有带修改和查询的二维/三维偏序问题.本文讲介绍多重CDQ分治的嵌套,即多维偏序问题. 四维偏序问题       给定N( ...

  7. 再见,OI

    你好,NOIP 2015年9月1日 正式成为了福建省莆田一中的一名高一成员 后来学校搞了选修 大家都很激动 因为自己的兴趣和特长能够得到发挥了(或者说能逃课或者看好多电影) 发现选修提供的选项中有好几 ...

  8. 告别我的OI生涯

    本文章写于2008年12月15日. 随着2008noip的结束,我也结束了我的OI生涯. 信息竞赛也许是从小到大让我最最努力的一件事.我记得参加2006noip初赛前,每天中午为了上信息课都吃不上中午 ...

  9. c++模板函数实例化的偏序机制

    一:废话 今天在stackoverflow上看到一个关于c++模板specialization的问题: http://stackoverflow.com/questions/18283851/temp ...

  10. 收集一些关于OI/ACM的奇怪的东西……

    一.代码: 1.求逆元(原理貌似就是拓展欧几里得,要求MOD是素数): int inv(int a) { if(a == 1) return 1; return ((MOD - MOD / a) * ...

随机推荐

  1. IP地址的精细化操作(将IP地址转为相对应的地址)

    第一步: 导入maven依赖: <dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2 ...

  2. 【Oracle】Windows-19C 下载安装

    下载 Download 官网下载地址[需要Oracle账号]: https://www.oracle.com/database/technologies/oracle-database-softwar ...

  3. 【DataBase】SQL50 Training 50题训练

    原文地址: https://blog.csdn.net/xiushuiguande/article/details/79476964 实验数据 CREATE DATABASE IF NOT EXIST ...

  4. pytorch-a2c-ppo-acktr-gail 算法代码

    地址: https://github.com/ikostrikov/pytorch-a2c-ppo-acktr-gail

  5. 如何理解计算机类论文、机器学习论文、人工智能AI论文中的“soft”和“hard”呢?

    如何理解计算机类论文.机器学习论文.人工智能AI论文中的"soft"和"hard"呢? 最近在看论文中总看到带有"soft"和"h ...

  6. 【转载】 在Ubuntu环境下,搜狗输入法乱码问题的解决

    原文作者:高坦的博客 | Tan's Blog 原文链接:https://www.cnblogs.com/gtscool/p/12234104.html本文采用 BY-NC-SA 许可协议.转载请注明 ...

  7. openAI的比赛retro contest的一些细节设置(Detail)

    2018年openAI公司搞了一个比赛retro contest,该比赛目的是为了在自家的库retro上测试迁移强化学习的性能,虽然这个比赛已经结束多年但是现在了解一些也是有一定益处的. 比赛细节介绍 ...

  8. 安装wsl的必备操作——开启CPU虚拟化——WslRegisterDistribution failed with error_ 0x8007019e 0x800701b 0x80370102 请启用虚拟机平台

    参考: https://www.cnblogs.com/smdtxz/p/16837946.html https://www.cnblogs.com/wenonly/p/17206040.html h ...

  9. Error in v-on handler: "TypeError: Cannot read property 'value' of undefined"

    Error in v-on handler: "TypeError: Cannot read property 'value' of undefined" 报错如下所示,即 在运行 ...

  10. 一种很变态但有效的DDD建模沟通方式

    本文书接上回<这就是为什么你学不会DDD>,关注公众号(老肖想当外语大佬)获取信息: 最新文章更新: DDD框架源码(.NET.Java双平台): 加群畅聊,建模分析.技术实现交流: 视频 ...