当前位置:编程学习 > C/C++ >>

[CF 61E]Enermy is weak[线段树求二重逆序数]

题意:

求单调下降的三元组的个数. 三个元素不一定要连续.

思路:

转化为求二重逆序数:

线段树求出逆序数, 再求逆序数序列的逆序数.


 

#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
typedef long long ll;
const int maxn = 1000005;
ll sum[maxn<<2];
typedef struct stru{
    int id;
    int val;
}stru;
stru  x[maxn];
int rev[maxn];
bool cmpid(stru a,stru b)
{
    return a.id < b.id;
}
bool cmpval(stru a,stru b)
{
    return a.val < b.val;
}

void PushUP(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    sum[rt] = 0;
    if (l == r) return ;
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
}

void update(int p,int l,int r,int rt)
{
    if (l == r)
    {
        sum[rt] ++;
        return ;
    }//更新的方法是在p位置上加1,于是向上的节点依次加1
    int m = (l + r) >> 1;
    if (p <= m) update(p , lson);
    else update(p , rson);
    PushUP(rt);
}

void updaterev(int id,int p,int l,int r,int rt)
{
    if (l == r)
    {
        sum[rt] = rev[id];
        //printf("x[%d] %d rev[%d] %I64d\n",id,x[id],id,rev[id]);
        return ;
    }//更新的方法是在p位置上加1,于是向上的节点依次加1
    int m = (l + r) >> 1;
    if (p <= m) updaterev(id, p , lson);
    else updaterev(id, p , rson);
    PushUP(rt);
}

ll query(int L,int R,int l,int r,int rt)
{
    if (L <= l && r <= R)
    {
        return sum[rt];
    }
    int m = (l + r) >> 1;
    ll ret = 0;
    if (L <= m) ret += query(L , R , lson);
    if (R > m) ret += query(L , R , rson);
    return ret;
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x[i].val);
        x[i].id = i;
    }
    sort(x,x+n,cmpval);
    for(int i=0;i<n;i++)    x[i].val = i;
    sort(x,x+n,cmpid);
    build(0 , n-1 , 1);
    for (int i = 0; i < n; i ++)
    {
///算法:将元素依次插入线段树,每个数的逆序对数为比它大的已经插入的数的个数
        rev[i] = query(x[i].val+1 , n-1 , 0 , n-1 , 1);///每个数的逆序数
        update(x[i].val , 0 , n-1 , 1);
    }
    ///需要求比x[i]大的数的逆序数之和!!!
    ll ans = 0;
    build(0, n-1, 1);
    for(int i=0;i<n;i++)
    {
        ans += query(x[i].val+1 , n-1 , 0, n-1 , 1);///已经插入的数的逆序数之和
        updaterev(i, x[i].val , 0, n-1 , 1);
    }
    printf("%I64d\n",ans);

    return 0;
}

 

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,