1. 程式人生 > >[JLOI2013]地形生成

[JLOI2013]地形生成

題目描述

最近IK正在做關於地形建模的工作。其中一個工作階段就是把一些山排列成一行。每座山都有各不相同的標號和高度。為了遵從一些設計上的要求,每座山都設定了一個關鍵數字,要求對於每座山,比它高且排列在它前面的其它山的數目必須少於它的關鍵數字。 顯然滿足要求的排列會有很多個。

對於每一個可能的排列,IK生成一個對應的標號序列和等高線序列。標號序列就是按順序寫下每座山的標號。

等高線序列就是按順序寫下它們的高度。例如有兩座山,這兩座山的一個合法排列的第一座山的標號和高度為1和3,而第二座山的標號和高度分別為2和4,那麼這個排列的標號序列就是1 2,而等高線序列就是3 4.

現在問題就是,給出所有山的資訊,IK希望知道一共有多少種不同的符合條件的標號序列和等高線序列。

輸入輸出格式

輸入格式:

輸入第一行給出山的個數N。接下來N行每行有兩個整數,按照標號從1到N的順序分別給出一座山的高度和關鍵數。

輸出格式:

輸出兩個用空格分隔開的數,第一個數是不同的標號序列的個數,第二個數是不同的等高線序列的個數。這兩個答案都應該對2011取模,即輸出兩個答案除以2011取餘數的結果

輸入輸出樣例

輸入樣例#1:

2
1 2
2 2

輸出樣例#1:

2 2

說明

對於所有的資料,有1<=N<=1000,所有的數字都是不大於109的正整數。


題解

給你一個序列,每個序列有權值和關鍵值
每個數前面比這個數的權值大的數少於關鍵值

第一問讓你求每個點都有標號的方案數
第二問讓你求相同高度的標號相同的方案數

我們把序列的排列轉化成一個一個數往序列裡放
這樣如果按照高度降序排序的話對於當前的點,之前所有的點都比它大
所以點i能插進的位置就是\(min(i,p[i].val)\)
這樣把每個點方案乘起來第一問就做完了

然後第二問可以轉化成一種一種高度往序列裡放
這樣的話我們就需要按照高度為第一關鍵字,val為第二關鍵字排序
這樣對於一段相同的高度\([l,r]\)
就相當於有n個無標號的球,m個盒子,每個盒子可以放多個球,第i個球只能放在第\([1,min(i-1,p[i].val-1)]\)個盒子中
這樣就可以用\(f[i][j]\)

表示前i個球放在了前j個盒子裡的方案數
\(f[i][j] = \sum_{k=0}^{k<=min(i-1,p[r].val-1}f[i - 1][k]\)
然後讓答案乘上\(\sum_{k=0}^{k<=min(i-1,p[r].val-1}{f[i][k]}\)就好辣

程式碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 1005 ;
const int mod = 2011 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n , Ans1 = 1 , Ans2 = 1 , Nxt , f[M] ;
struct Hill { int val , High ; } p[M] ;
bool CmpHv(Hill a , Hill b) 
{ return a.High == b.High ? a.val < b.val : a.High > b.High ; }
int main() {
    n = read() ;
    for(int i = 1 ; i <= n ; i ++) p[i].High = read() , p[i].val = read() - 1 ;
    sort(p + 1 , p + n + 1 , CmpHv) ;
    for(int i = 1 ; i <= n ; i = Nxt + 1) {
        Nxt = i ;
        while(p[Nxt + 1].High == p[i].High) ++ Nxt ;
        memset(f , 0 , sizeof(f)) ; f[0] = 1 ; int ret = 0 ;
        for(int j = i ; j <= Nxt ; j ++) {
            Ans1 = (Ans1 * (min(p[j].val + 1 , i) + j - i)) % mod ;
            for(int k = 1 ; k <= min(i - 1 , p[j].val) ; k ++) 
                f[k] = (f[k] + f[k - 1]) % mod ;
        }
        for(int j = 0 ; j <= min(i - 1 , p[Nxt].val) ; j ++) ret = (ret + f[j]) % mod ;
        Ans2 = (Ans2 * ret) % mod ;
    }
    cout << Ans1 << " " << Ans2 << endl ;
    return 0 ; 
}