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

找了4道题目
BZOJ陌上花开(权限题,提供洛谷链接)
Cogs2479偏序
Cogs2580偏序II
Cogs2639偏序++


作为一个正常人,肯定先看三维偏序
做法很多呀
首先,由于

智商不够数据结构来补        –菊开

所以我们用最傻逼的数据结构来做这道题目
第一维:排序
第二维:树状数组
第三维:平衡树
于是乎,我们得到了一个复杂度为 O(nlog2n) 的做法
并且常数巨大
这个做法到这里去看


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

这里同理,第一维排序
第二维类似于归并排序就行合并
同时一遍归并一边计算第三维

是不是想到了逆序对还可以用树状数组来求
所以这里一样的,
第三维用一个BIT来求就行了

这种做法看这里

恩,三维偏序没了


现在看Cogs的【偏序】

四维偏序模板题
可以顺着三维偏序来思考
既然多了一维
我就多套个CDQ分治呀
那不就是CDQ分治套树套树
或者CDQ分治套CDQ分治加树状数组
复杂度 O(nlog3n)
好像也可以诶。。。


好好好
我们继续
Cogs【偏序II】
不就是个五维偏序吗
CDQ套CDQ套CDQ
或者CDQ套CDQ套树套树
没毛病,时间复杂度 O(nlog4n)
可以做可以做,没问题的


那,我们继续
Cogs【偏序++】
七维偏序呀
CDQ套CDQ套CDQ套CDQ套CDQ
或者CDQ套CDQ套CDQ套CDQ套树套树
时间复杂度 O(nlog6n)
绝对跑不过的
而且给定的维数是K
你还得分类讨论,看着就是不可做的题目呀。。。
怎么办怎么办。。。


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

这是他的PPT


FHR的做法的复杂度: O(nn)
首先,我们知道
一个向量的偏序数量
等于它每一维的偏序数量的交集的大小
所以,按照每一维排序之后
我们就可以求出比每一维小的集合
然后求一个交集就行了

但是,暴力求,,,不现实吧。
而且交集也不好求呀
所以,来搞个 bitset
STL大法好
但是,这样不就是 O(n2) 的暴力吗???
没错,我们再来一发——分块大法好
分块之后就可以把一个 n 变成n
于是复杂度就降到了 O(nn)
美滋滋
献上丑陋的代码(【偏序++】)

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值