gym101485G/BZOJ4430 Guessing Camels赌骆驼 树状数组或CDQ分治

Guessing Camels

题意:
给出 1~n 打乱排序的三个序列,计算在三个序列中都是同样顺序的数字对数。
tags:
好像可以CDQ分治,但不会。。。只会树状数组的
参考了大佬的题解:https://blog.csdn.net/braketbn/article/details/51392650

我们反过来考虑,满足条件的对数 = 总对数 - 不满足条件的对数。
总对数好算,就是 n*(n-1)/2 。
不满足的对数,也就是说两个数 (a,b),在其中两种排序中是相同的顺序,但在第三种排序中是不同的顺序。
所以对于每两个序列,计算一下不满足偏序关系的对数,这样每对数都被计算了两次,最后不满足的就是除 2 。

每两个序列之间,具体这样考虑:
考虑下面这种情况,对于数对(x, y)
第一个排列:_x_u_y___k
第二个排列:___y_____x
(u与y下标相同,k与x下标相同)
我们从后向前遍历第一个排列。在遍历到k的时候,查询一下在第一个排列里x位置之前的前缀和,然后再在第一个排列里x位置+1。
遍历到u时,查询一下第一个排列里y位置之前的前缀和(这样就统计到了x位置上的1),然后再在第一个排列里y位置+1。

// gym101485G
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 200005;

ll  bit[N];
void Add(int x, ll y) {
    for( ; x<N; x+=x&-x) bit[x]+=y;
}
ll  Sum(int x) {
    ll ret=0; for( ; x; x-=x&-x) ret+=bit[x]; return ret;
}

int n, pos[N];
ll  a[3][N];
ll  solve(ll* a1, ll* a2)
{
    ll  ret = 0;   mes(bit,0);
    rep(i,1,n) pos[a1[i]] = i;
    per(i,n,1) ret+=Sum(pos[a2[i]]), Add(pos[a2[i]], 1);
    return ret;
}
int main()
{
    scanf("%d", &n);
    rep(i,0,2) rep(j,1,n) scanf("%lld", &a[i][j]);
    ll  ans = 1LL*n*(n-1)/2;
    ans -= (solve(a[0],a[1])+solve(a[0],a[2])+solve(a[1],a[2]))/2;
    printf("%lld\n", ans);

    return 0;
}
posted @ 2018-05-10 15:14  v9fly  阅读(134)  评论(0编辑  收藏  举报