[COGS 2479] [HZOI 2016] 偏序

COGS传送门

【题目描述】

给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足 i<j i < j ai<aj a i < a j bi<bj b i < b j ci<cj c i < c j 的数对 (i,j) ( i , j ) 的个数。

【输入格式】

第一行一个整数 n n ,表示序列长度。

第二行 n n 个整数,分别表示 a1an a 1 ∼ a n

第三行 n n 个整数,分别表示 b1bn b 1 ∼ b n

第四行 n n 个整数,分别表示 c1cn c 1 ∼ c n

【输出格式】

一个整数,表示答案。

【样例输入】

5
1 5 3 4 2
2 5 3 4 1
1 2 5 3 4

【样例输出】

3

【样例解释】

满足条件的 (i,j) ( i , j ) 共有以下三对:

(1,2) ( 1 , 2 )

(1,3) ( 1 , 3 )

(1,4) ( 1 , 4 )

【数据范围与约定】

对于30%的数据, n5000 n ≤ 5000

对于100%的数据, 1n50000 1 ≤ n ≤ 50000 ,保证所有的 aibici a i 、 b i 、 c i 分别组成三个 1n 1 ∼ n 的排列。

【来源】

HZOI 2016

解题分析

四维偏序模板题…

我们当然可以 CDQ C D Q 套树套树, 但是在这里介绍一种 CDQ C D Q CDQ C D Q 的写法。

第一维:我们通过排序解决。(在这里就是输入顺序)

第二维:我们 CDQ C D Q buf b u f 数组里实现对其的排序, 递归处理。 同时处理 [lef,rig] [ l e f , r i g ] 区间时, 将 [lef,mid] [ l e f , m i d ] 区间打上标记表示其第一维较小。

第三维:我们 CDQ C D Q buf2 b u f 2 数组里实现对其的排序。 当然 buf2 b u f 2 是基于 buf b u f 得到的。

第四维: BIT B I T 维护前缀和即可。 注意修改、查询的条件是第一维第二维都较小。 这时我们就可以利用打的标记来判定了。

如果还是不懂的同学可以看代码…

#include 
#include 
#include 
#include 
#include 
#include 
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 50050
#define File freopen("partial_order.in", "r", stdin), freopen("partial_order.out", "w", stdout)
#define lbt(i) (i & -i)
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
struct Node
{
    int a, b, c, d;
    bool typ;
}eve[MX], buf[MX], buf2[MX];
int dot, tree[MX];
long long ans;
namespace BIT
{
    IN void clear(R int now)
    {
        W (now <= dot)
        if(tree[now]) tree[now] = 0, now += lbt(now);
        else return;
    }
    IN void add(R int now)
    {W (now <= dot) ++tree[now], now += lbt(now);}
    IN int query(R int now)
    {
        int ret = 0;
        W (now) ret += tree[now], now -= lbt(now);
        return ret;
    }
}
void cdq2(const int &lef, const int &rig)
{
    if(lef == rig) return;
    int mid = lef + rig >> 1;
    cdq2(lef, mid), cdq2(mid + 1, rig);//先递归保证左右区间c单增, 并且左区间b值小于右区间b值
    int lb = lef, rb = mid + 1, cur = lef;
    W (lb <= mid && rb <= rig)
    {
        if(buf[lb].c < buf[rb].c)
        {
            if(!buf[lb].typ) BIT::add(buf[lb].d);//必须a值更小才能更新
            buf2[cur++] = buf[lb++];
        }
        else
        {
            if(buf[rb].typ) ans += BIT::query(buf[rb].d);//必须a值更大才能计入结果
            buf2[cur++] = buf[rb++];
        }
    }
    W (lb <= mid) buf2[cur++] = buf[lb++];
    W (rb <= rig)
    {
        if(buf[rb].typ) ans += BIT::query(buf[rb].d);
        buf2[cur++] = buf[rb++];
    }
    for (R int i = lef; i <= mid; ++i) if(!buf[i].typ) BIT::clear(buf[i].d);
    for (R int i = lef; i <= rig; ++i) buf[i] = buf2[i];
}
void cdq1(const int &lef, const int &rig)//处理b
{
    if(lef == rig) return;
    int mid = lef + rig >> 1;
    cdq1(lef, mid), cdq1(mid + 1, rig);//先递归保证左右区间b单增
    int lb = lef, rb = mid + 1, cur = lef;
    W (lb <= mid && rb <= rig)
    {
        if(eve[lb].b < eve[rb].b) buf[cur++] = eve[lb++], buf[cur - 1].typ = false;
        else buf[cur++] = eve[rb++], buf[cur - 1].typ = true;
    }
    W (lb <= mid) buf[cur++] = eve[lb++], buf[cur - 1].typ = false;
    W (rb <= rig) buf[cur++] = eve[rb++], buf[cur - 1].typ = true;
    for (R int i = lef; i <= rig; ++i) eve[i] = buf[i];
    cdq2(lef, rig);
}
int main(void)
{
    File;
    in(dot);
    for (R int i = 1; i <= dot; ++i) in(eve[i].b), eve[i].a = i;
    for (R int i = 1; i <= dot; ++i) in(eve[i].c);
    for (R int i = 1; i <= dot; ++i) in(eve[i].d);
    cdq1(1, dot);
    printf("%lld", ans);
}

你可能感兴趣的:(CDQ,分治)