計蒜客 奇怪的報數遊戲

題目描述

奇怪的報數遊戲

大致思路

樹狀數組 + 二分
這裏先給出一個公式 第 i 個數的編號= i 之前比 i 小的總數量+ i 之後比 i 小的總數量+1

例如 1 4 3 6 2 5 這個序列,我們可以用這個序列來驗證上述的公式,比如 3 = 1 (3前邊只有1比3小,所以只有1個) + 1 (3後邊只有2比3小,所以只有1個) + 1,對吧。

已經找出來的編號我們可以存進樹狀數組中,這樣我們就可以直接通過getsum(x)直接查詢比x小的數總共有多少個數。

我們從後往前推,最後一個數可以直接推出來,它就等於前邊比他小的數+1(因爲他後面沒有數就可以不用加了 笑哭臉.jpg)

倒數第二個數呢, ?(他的編號,還不知道,所有先用問號代替)=num[ n-1 ](他前面比他小的數,這個數題目給了的)+ ?(他後面比他小的數,同樣也是不知道)+1,雖然我們現在不知道他的編號是多少,但我們可以通過二分思想把他的編號找出來,可以看下下面代碼

l=1;r=n+1;
        while(l<r){//二分思想確定下標
            mid=(l+r)>>1;
            if(mid>getsum(mid-1)+num[i]+1)//數>(後面小於次數的人數)+(前面小於次數對的人數,也就是num[i])+1
                r=mid;//此編號偏大
            else
                l=mid+1;//此編號偏小
        }
        change(r-1);//找出編號,編號等於r-1
        ans[i]=r-1;
        flag[r-1]=true;

這樣就可以求出倒數第二個數,其實這個也可以求出倒數第一數,因爲第一個數前邊有多少數沒有給出來,所以這樣就可以一直求到第二個數,現在就只剩一個數,很簡單的,我在這裏就不提示了(實在想不出來可以看看我的flag數組)

ac代碼

#include<cstdio>
#include<cstring>

const int MAXN=1e5+1;
int ans[MAXN],c[MAXN],num[MAXN],n;
bool flag[MAXN];

int getsum(int x){//求前x和
    int re=0;
    for(;x;x-=x&(-x))
        re+=c[x];
    return re;
}
void change(int x){//改變樹狀數組的值
    for(;x<=n;x+=x&(-x))
        c[x]++;
    return;
}

int main()
{
    scanf("%d",&n);
    memset(flag,false,sizeof(flag));//設置標記找第一個人的序號
    memset(c,0,sizeof(c));
    for(int i=2;i<=n;i++)
        scanf("%d",&num[i]);
    int l,r,mid;
    for(int i=n;i>=2;i--){
        l=1;r=n+1;
        while(l<r){//二分思想確定下標
            mid=(l+r)>>1;
            if(mid>getsum(mid-1)+num[i]+1)//數>(後面小於次數的人數)+(前面小於次數對的人數,也就是num[i])+1
                r=mid;//此數偏大
            else
                l=mid+1;//次數偏小
        }
        change(r-1);//找到數字,數字等於r-1
        ans[i]=r-1;
        flag[r-1]=true;
    }
    for(int i=1;i<=n;i++){//爲第一個人找編號
        if(!flag[i]){
            ans[1]=i;
            break;
        }
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;//give me five
}