1. 程式人生 > >51nod 1376: 最長遞增子序列的數量(二維偏序+cdq分治)

51nod 1376: 最長遞增子序列的數量(二維偏序+cdq分治)

col pac def esp 調用 sha oid 題目 數字


1376 最長遞增子序列的數量

  Time Limit: 1 Sec   Memory Limit: 128MB
  分值: 160        難度:6級算法題

Description

  數組A包含N個整數(可能包含相同的值)。設S為A的子序列且S中的元素是遞增的,則S為A的遞增子序列。如果S的長度是所有遞增子序列中最長的,則稱S為A的最長遞增子序列(LIS)。A的LIS可能有很多個。例如A為:{1 3 2 0 4},1 3 4,1 2 4均為A的LIS。給出數組A,求A的LIS有多少個。由於數量很大,輸出Mod 1000000007的結果即可。相同的數字在不同的位置,算作不同的,例如 {1 1 2} 答案為2。

Input

  第1行:1個數N,表示數組的長度。(1 <= N <= 50000)
  第2 - N + 1行:每行1個數A[i],表示數組的元素(0 <= A[i] <= 10^9)

Output

  輸出最長遞增子序列的數量Mod 1000000007。

Sample Input

  5
  1
  3
  2
  0
  4

Sample Output

  2

題目地址: 51nod 1376: 最長遞增子序列的數量

題目大意: 求最長上升子序列的個數

www.cnblogs.com/shaokele/


題解:

  顯然這道題目有很多做法 (知乎)
  然而這裏只做cdq分治
  LIS其實是一個二維偏序問題(下標和值)

  分治的套路:
  第一維排序,第二維分治
  1. 先遞歸求解區間左半邊
  2. 用左邊的值更新右邊,相當於處理跨越區間中點的情況
  3. 遞歸求解區間右半邊
  

//by:知乎 李貝瑀

bool cmp(int i, int j) { return a[i] != a[j]? a[i] < a[j]: i > j; }

void cdq(int l, int r)
{
        if (l == r) {
                if (!f[l]) f[l] = 1;
                return;
        }
        int mid = (l + r) >> 1;
        cdq(l, mid);
        for (int i = l; i <= r; ++i) id[i] = i;
        sort(id + l, id + r + 1, cmp);
        int len = 0;
        for (int i = l; i <= r; ++i) {
                if (id[i] <= mid) len = max(len, f[id[i]]);
                else f[id[i]] = max(f[id[i]], len + 1);
        }
        // code
        cdq(mid + 1, r);
}

  上面這個寫法其實是 \(O(Nlog^2N)\) 的,因為對id排序時調用了sort,可以改成歸並排序

  對於求解方案數,只需要在註釋的地方加上統計的代碼
  過程和前面基本類似,能理解的話很容易寫出來


AC代碼

#include <cstdio> 
#include <algorithm>
#define mp make_pair
#define pr pair<int,int>
using namespace std;
const int N=50005,mo=1000000007;
int n;
int a[N],id[N];
pr f[N];
bool cmp(int x,int y){
    if(a[x]!=a[y])return a[x]<a[y];
    return x>y;
}
pr Max(pr a,pr b){
    if(a.first>b.first)return a;
    if(a.first<b.first)return b;
    if((a.second+=b.second)>=mo)
        a.second-=mo;
    return a;
}
void cdq(int l,int r){
    if(l==r)return;
    int mid=(l+r)/2;
    cdq(l,mid);
    for(int i=l;i<=r;i++)id[i]=i;
    sort(id+l,id+r+1,cmp);
    pr mx=mp(0,0);
    for(int i=l;i<=r;i++){
        int k=id[i];
        if(k<=mid)mx=Max(mx,f[k]);
        else{
            pr now=mx;
            now.first++;
            f[k]=Max(f[k],now);
        }
    }
    cdq(mid+1,r);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        f[i]=mp(1,1);
    cdq(1,n);
    pr ans=mp(0,0);
    for(int i=1;i<=n;i++)
        ans=Max(ans,f[i]);
    printf("%d\n",ans.second);
    return 0;
}

51nod 1376: 最長遞增子序列的數量(二維偏序+cdq分治)