程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> [CF 61E]Enermy is weak[線段樹求二重逆序數]

[CF 61E]Enermy is weak[線段樹求二重逆序數]

編輯:C++入門知識

題意:

求單調下降的三元組的個數. 三個元素不一定要連續.

思路:

轉化為求二重逆序數:

線段樹求出逆序數, 再求逆序數序列的逆序數.


 

#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;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved