(其实,,,我是来学二维偏序的,,,结果被卿姐的视频(尽管这次主讲人不是卿姐)带偏了,,结果就都学了,,,)
既然要学习多维偏序,那就先看一下偏序是个什么鬼吧,,,,
偏序:
给定一个集合
S
S
S,“
≤
≤
≤”是
S
S
S上的二元关系,若“
≤
≤
≤”满足:
1.自反性:
∀
a
∈
S
∀a∈S
∀a∈S,有
a
≤
a
a≤a
a≤a;
2.反对称性:∀a,b∈S,
a
≤
b
a≤b
a≤b且
b
≤
a
b≤a
b≤a,则
a
=
b
a=b
a=b;
3.传递性:
∀
a
,
b
,
c
∈
S
,
a
≤
b
∀a,b,c∈S,a≤b
∀a,b,c∈S,a≤b且
b
≤
c
b≤c
b≤c,则
a
≤
c
a≤c
a≤c;
则称“
≤
≤
≤”是
S
S
S上的非严格偏序或自反偏序。
(可以手动百度百科一下:偏序)
知道什么是偏序后就来考虑一下怎么搞定偏序问题:
1.一维偏序:
(很显然,不就是个sort嘛,,不多说了)
2.二维偏序:
这个要想一下了,放一个二维偏序的板子题:Laptop
二维偏序就可以看作是一个坐标轴中的点(x,y)就是查找有多少个点x,y都比该点小。
这个,做过最长上升子序列,就知道怎么坐了,
先按x排个序,再倒着枚举树状数组求后缀和,树状数组更新即可。
放代码:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int sea=1e5+7;
struct hit{int x,y;}a[sea];
int n,lb,b[sea],c[sea];
bool cmp(hit a,hit b){return a.x<b.x;}
int lowbit(int x){return x&-x;}
void add(int x){for(int i=x;i<=sea;i+=lowbit(i)) c[i]++;}
int ask(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=c[i];return ans;}
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),b[i]=a[i].y;
sort(b+1,b+n+1); lb=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++) a[i].y=lower_bound(b+1,b+lb+1,a[i].y)-b;
sort(a+1,a+n+1,cmp);
int ans=0;
for(int i=n;i>=1;i--)
{
int ins=ask(a[i].y);
if(n-i-ins!=0) ans++;
add(a[i].y);
}
printf("%d",ans);
return 0;
}
3.三维偏序:
既然二维偏序都会了,就来看一下三维偏序吧,,
给你三个维度你怎么办呢,,,
引入一个新知:CDQ分治(陈丹琪分治)
CDQ分治,好东西啊,这个没有什么固定的板子,是个比较巧的思想。
先介绍一下CDQ的基本思想:
当对于一个序列来说,如果说你已经处理好了前面一部分的结果,你就可以用前面一部分的信息去处理对后面所造成的影响,
(尽管它叫CDQ分治,但是我觉得和分治的中心思想其实差不多,也有大佬说:不是她发明的,这就是分治,只不过是因为cdq将她推广了起来)
这样的话,,我们就可以对第一维进行CDQ分治,第二维树状数组,第三维就sort即可,,
放个例题(经典的板子题):陌上花开
代码:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int sea=2e5+7;
struct hit{
int x,y,z,id,cnt,ans;
bool operator==(hit A) const {return x==A.x&&y==A.y&&z==A.z;}
}a[sea],b[sea];
int nn,n,k,c[sea];
bool cmp1(hit a,hit b){return a.x<b.x||(a.x==b.x&&(a.y<b.y||(a.y==b.y)&&(a.z<b.z)));}
bool cmp2(hit a,hit b){return a.y<b.y||(a.y==b.y&&(a.z<b.z||(a.z==b.z)&&(a.x<b.x)));}
int lowbit(int x){return x&-x;}
void add(int x,int a){for(int i=x;i<=k;i+=lowbit(i)) c[i]+=a;}
int ask(int x){int s=0;for(int i=x;i;i-=lowbit(i)) s+=c[i];return s;}
void CDQ(int L,int R)
{
if(L==R) return ;
int mid=(L+R)/2; CDQ(L,mid);CDQ(mid+1,R);
sort(a+L,a+R+1,cmp2);
for(int i=L;i<=R;i++)if(a[i].id<=mid) add(a[i].z,a[i].cnt); else a[i].ans+=ask(a[i].z);
for(int i=L;i<=R;i++) if(a[i].id<=mid) add(a[i].z,-a[i].cnt);
}
int main()
{
nn=read(); k=read(); n=0;
for(int i=1;i<=nn;i++) b[i].x=read(),b[i].y=read(),b[i].z=read();
sort(b+1,b+nn+1,cmp1);
for(int i=1;i<=nn;i++)
if(b[i]==a[n]) a[n].cnt++;
else a[++n]=b[i],a[n].cnt=1,a[n].id=n;
CDQ(1,n);
sort(a+1,a+1+n,cmp1);
for(int i=1;i<=n;i++) c[a[i].cnt+a[i].ans]+=a[i].cnt;
for(int i=1;i<=nn;i++) printf("%d\n",c[i]);
return 0;
}
4.四维偏序:
直接放题了:
题目链接:Star
题意:询问三维空间中的两颗星星之间的星星,单点插入,区间查询。
题解:
(尽管是四维偏序的板子题,,,我还是敲了快两个小时,,,)
还是套路:一维,二维CDQ分治,三维树状数组,四维sort。
但是这道题还是要用点容斥的,因为是两个三维星星之间,所以对于询问要容斥处理一下。
代码:
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int read()
{
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch<='9'&&ch>='0')s=s*10+ch-'0',ch=getchar();
return s*w;
}
struct hit
{
int x,y,z,typ,id;
hit (int xx,int yy,int zz,int tt,int ii):x(xx),y(yy),z(zz),typ(tt),id(ii) {}
hit (){}
};
const int sea=5e4+7;
const int pool=1e5+7;
vector<hit>v,v2,v3;
vector<int>lsh;
int T,n,m,ans[sea],c[pool],vis[sea];
bool cmpv2(hit a,hit b){if(a.x==b.x) return a.id<b.id;else return a.x<b.x;}
bool cmpv3(hit a,hit b){if(a.y==b.y) return a.id<b.id;else return a.y<b.y;}
int lowbit(int x){return x&-x;}
void add(int x,int a){for(int i=x;i<pool;i+=lowbit(i)) c[i]+=a;}
int ask(int x){int s=0;for(int i=x;i;i-=lowbit(i)) s+=c[i];return s;}
void Star()
{
for(int i=0;i<v3.size();i++)
{
if(v3[i].typ==0) add(v3[i].z,1);
else
{
int t=ask(v3[i].z);
ans[v3[i].id]+=t*v3[i].typ;
}
}
for(int i=0;i<v3.size();i++) if(v3[i].typ==0) add(v3[i].z,-1);
}
void CDQ2(int l,int r)
{
if(l>=r) return ;
int mid=(l+r)/2;
CDQ2(l,mid); v3.clear();
for(int i=l;i<=mid;i++) if(v2[i].typ==0) v3.push_back(v2[i]);
for(int i=mid+1;i<=r;i++) if(v2[i].typ!=0) v3.push_back(v2[i]);
sort(v3.begin(),v3.end(),cmpv3);
Star();
CDQ2(mid+1,r);
}
void CDQ(int l,int r)
{
if(l>=r) return ;
int mid=(l+r)/2;
CDQ(l,mid); v2.clear();
for(int i=l;i<=mid;i++) if(v[i].typ==0) v2.push_back(v[i]);
for(int i=mid+1;i<=r;i++) if(v[i].typ!=0) v2.push_back(v[i]);
sort(v2.begin(),v2.end(),cmpv2);
CDQ2(0,v2.size()-1);
CDQ(mid+1,r);
}
int main()
{
T=read();
while(T--)
{
memset(ans,0,sizeof(ans));
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
v.clear(); lsh.clear();
n=read();
for(int i=0;i<n;i++)
{
int op=read();
if(op==1)
{
int x=read(),y=read(),z=read();
v.push_back(hit(x,y,z,0,i));
lsh.push_back(z);
}
else
{
vis[i]=1;
int xa=read(),ya=read(),za=read(),xb=read(),yb=read(),zb=read();
v.push_back(hit(xa-1,ya-1,za-1,-1,i));
v.push_back(hit(xa-1,ya-1,zb,1,i));
v.push_back(hit(xa-1,yb,za-1,1,i));
v.push_back(hit(xa-1,yb,zb,-1,i));
v.push_back(hit(xb,ya-1,za-1,1,i));
v.push_back(hit(xb,ya-1,zb,-1,i));
v.push_back(hit(xb,yb,za-1,-1,i));
v.push_back(hit(xb,yb,zb,1,i));
lsh.push_back(za-1); lsh.push_back(zb);
}
}
sort(lsh.begin(),lsh.end()); auto it=unique(lsh.begin(),lsh.end());//auto是比较松的定义类型,可以对你所赋的初值进行自行定义类型,但是只有在c++.11中可以通过编译,可以自行百度依下。
lsh.resize(distance(lsh.begin(),it));
for(int i=0;i<v.size();i++) v[i].z=lower_bound(lsh.begin(),lsh.end(),v[i].z)-lsh.begin()+1;
CDQ(0,v.size()-1);
for(int i=0;i<n;i++) if(vis[i]) printf("%d\n",ans[i]);
}
return 0;
}
以为只有四维就最高了????
你想吧,,,,( ̄ェ ̄;)
4.五维偏序及以上
北京的一道十六维偏序就死了好多人,,,,
放一下链接:上天的卿学姐(十六维偏序)
但其实是分块+CDQ分治+树状数组即可
据dalao说都是用分块写的,,而且还很简单,,
(我这个蒟蒻就再去好好学学,学会了马上补,,一定补!(心虚))