几道很Interesting的偏序问题

时间:2023-03-09 01:48:06
几道很Interesting的偏序问题

若干道偏序问题(STL,分块)

找了4道题目

BZOJ陌上花开(权限题,提供洛谷链接)

Cogs2479偏序

Cogs2580偏序II

Cogs2639偏序++


作为一个正常人,肯定先看三维偏序

做法很多呀

首先,由于

智商不够数据结构来补 $ \ \ \ \ \ \ $--菊开

所以我们用最傻逼的数据结构来做这道题目

第一维:排序

第二维:树状数组

第三维:平衡树

于是乎,我们得到了一个复杂度为\(O(nlog^2n)\)的做法

并且常数巨大

这个做法到这里去看


第二种做法
CDQ分治
相信大家都会逆序对的求法
可以归并排序求逆序对
逆序对相当于是一个二维偏序
第一维就相当于它的位置
第二维就是值
每次归并排序的时候相当于分成了两部分
只有左侧的部分才能对右侧的部分产生贡献

这里同理,第一维排序

第二维类似于归并排序就行合并

同时一遍归并一边计算第三维

是不是想到了逆序对还可以用树状数组来求

所以这里一样的,

第三维用一个BIT来求就行了

这种做法看这里

恩,三维偏序没了


现在看Cogs的【偏序】

四维偏序模板题

可以顺着三维偏序来思考

既然多了一维

我就多套个CDQ分治呀

那不就是CDQ分治套树套树

或者CDQ分治套CDQ分治加树状数组

复杂度\(O(nlog^3n)\)

好像也可以诶。。。


好好好

我们继续

Cogs【偏序II】

不就是个五维偏序吗

CDQ套CDQ套CDQ

或者CDQ套CDQ套树套树

没毛病,时间复杂度\(O(nlog^4n)\)

可以做可以做,没问题的


那,我们继续

Cogs【偏序++】

七维偏序呀

CDQ套CDQ套CDQ套CDQ套CDQ

或者CDQ套CDQ套CDQ套CDQ套树套树

时间复杂度\(O(nlog^6n)\)

绝对跑不过的

而且给定的维数是K

你还得分类讨论,看着就是不可做的题目呀。。。

怎么办怎么办。。。


首先,请允许我orz大佬 ——中国翅王

几道很Interesting的偏序问题

这是他的PPT


FHR的做法的复杂度:\(O(n\sqrt n)\)

首先,我们知道

一个向量的偏序数量

等于它每一维的偏序数量的交集的大小

所以,按照每一维排序之后

我们就可以求出比每一维小的集合

然后求一个交集就行了

但是,暴力求,,,不现实吧。

而且交集也不好求呀

所以,来搞个\(bitset\)

STL大法好

但是,这样不就是\(O(n^2)\)的暴力吗???

没错,我们再来一发——分块大法好

分块之后就可以把一个\(n\)变成\(\sqrt n\)

于是复杂度就降到了\(O(n \sqrt n)\)

美滋滋

献上丑陋的代码(【偏序++】)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<bitset>
#include<queue>
using namespace std;
#define MAX 45000
inline int read()
{
int x=0,t=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n,blk;
bitset<45000> bs[15][250];
pair<int,int> val[15][MAX];
int bl[MAX],bll[MAX],blr[MAX];
int K,num;
long long Ans;
int f[15][MAX];
int find(int k,int x)
{
int l=1,r=n,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(val[k][mid].first<=x)ans=x,l=mid+1;
else r=mid-1;
}
return ans;
}
inline bitset<MAX> getbst(int p,int x)
{
bitset<MAX> ans;ans.reset();
int pp=find(p,x);
if(pp<=0)return ans;
int pre=(pp-1)/blk;
int st=pre*blk+1;
ans=bs[p][pre];
for(int i=st;i<=pp;++i)ans.set(val[p][i].second);
return ans;
}
void Solve()
{
bitset<MAX> ans;ans.reset();
for(int i=1;i<=n;++i)
{
ans.set();
for(int j=1;j<=K;++j)
ans&=getbst(j,f[j][i]);
Ans+=ans.count()-1;
}
}
int main()
{
freopen("partial_order_plus.in","r",stdin);
freopen("partial_order_plus.out","w",stdout);
n=read();blk=sqrt(n);K=read()+1;
for(int i=1;i<=n;++i)val[1][i]=make_pair(f[1][i]=i,i);
for(int j=2;j<=K;++j)
for(int i=1;i<=n;++i)
val[j][i]=make_pair(f[j][i]=read(),i);
for(int i=1;i<=K;++i)sort(&val[i][1],&val[i][n+1]);
for(int i=1;i<=n;++i)
bl[i]=(i-1)/blk+1;num=bl[n];
for(int i=1;i<=num;++i)
bll[i]=(i-1)*blk+1,blr[i]=i*blk;blr[num]=n;
for(int j=1;j<=K;++j)
for(int i=1;i<=num;++i)
{
bs[j][i]=bs[j][i-1];
for(int k=bll[i];k<=blr[i];++k)
bs[j][i][val[j][k].second]=1;
}
Solve();
printf("%lld\n",Ans);
return 0;
}