有n(≤1300)棟摩天樓,從左往右排成一行。第i棟的高度是i個單位,並且每棟摩天樓都有顏色(有些摩天樓的顏色相同),將這些摩天樓排列,有多少種排列的方法,使得從左往右看去,看到不多不少剛好l棟摩天樓,答案mod 1000000009。
這裡定義一下「看到」,首先若某棟摩天樓左邊有比它高的,那就看不到。如果從左往右依次連續看到的兩棟(或以上)摩天樓顏色相同,會誤認為是一棟。
由於這個關係到次序問題,所以dp的順序可以按照從大到小的順序來,這裡只需要把讀入的順序倒過來(讀入是從小到大,下面的描述第i棟的高度是n - i + 1)。
記f(j,i)為能夠看到j棟摩天樓,並且放置了前i棟摩天樓後,第一棟看到的樓就是第i棟樓。求出f(j,i)需要列舉前一棟看到的樓是什麼,假設看到的是k這棟樓:
如果k和i的顏色是一樣的,那麼會誤當成一棟,同時k + 1至i - 1這些樓需要放在k的後面,因為它們都比k矮,那麼前一棟看到的就不是k了,那麼,k + 1至i - 1這些樓放置的方案數為(假設下標為1),k * (k + 1) * (k + 2) * … * (k + i - 2),若k + 2 等於 i,那麼就什麼都不用乘,因為i,k之間沒有其他樓
:f(j,i) += f(j,k) * k * (k + 1) * (k + 2) * … * (k + i - 2);
如果顏色不一樣,那麼就能多看到一棟樓:f(j,i) += f(j - 1,k) * k * (k + 1) * (k + 2) * … * (k + i - 2)。
時間複雜度:首先n^2種狀態,轉移o(n),總為o(n^3)。
我們需要再優化一下轉移,可以畫個**來尋找方法:
摩天樓1,3,6是同一種顏色,而1,3,5,6中沒有乙個的顏色和摩天樓7相同,它們的顏色也不一定相同。現在若需要求f(3,7)(藍色部分),它的值等於:
f(3, 7) =
+ f
(2, 1) * 1 * 2 * 3 * 4 * 5
+ f
(3, 2) * 2 * 3 * 4 * 5
+
f(2, 3) * 3 * 4 * 5
+ f(3, 4) * 4 * 5 +
f(2, 5) * 5
+ f(2, 6)
把它分成黃色,綠色,藍色三個部分求解。
再來看看它前乙個同種顏色的摩天樓(第4棟):
f(3, 4) =
+ f(2, 1) * 1 * 2 * 3
+ f(3, 2) * 2 * 3 +
f(2, 3) * 3
看到沒有,f(3, 7)劃線部分的值和f(3, 4)是一樣的。並且加粗部分是劃線部分的4 * 5倍,黃色部分的值就求出來了!至於i * (i + 1)* … * j,可以預處理。然後,f(3, 4) * 4 * 5可以單獨算(綠色部分)。接著,就剩下f(2, 5) * 5 + f(2, 6)。既然第4棟摩天樓是前乙個和7同顏色的,那麼,4 + 1至7 - 1的樓都不和7相同顏色,這就可以用類似於部分和的東西。
由於公式編輯麻煩,這裡就不貼了,找找規律就知道了。
那麼g(2, 6) =
+ f(2, 1) * 1 * 2 * 3 * 4 * 5
+ f(2, 2) * 2 * 3 * 4 * 5
+
f(2, 3) * 3 * 4 * 5
+ f(2, 4) * 4 * 5 +
f(2, 5) * 5
+ f(2, 6)
g(2, 4) =
+ f(2, 1) * 1 * 2 * 3
+ f(2, 2) * 2 * 3 +
f(2, 3) * 3 +
f(2, 4)
那麼我們所需要求的f(2, 5) * 5 + f(2, 6)就等於g(2, 6)減去4 * 5倍的g(2, 4)(橙色部分)。
至於g如何求,這應該很簡單吧。g(j,i) = g(j,i-1) * i + f(j, i)。
這樣,我們就可以o(1)實現求解某個f(j, i)了。問題就到此結束了,時間複雜度就是o(n^2)。
我的**下標是從0開始的,寫的一般般:
#include #include #include using namespace std;
typedef long long ll;
const ll mod = 1000000009;
const int n = 1300, c = 2707;
char x[n];
int n, l, col[n]; // 第i棟樓的顏色編號
int pre[c]; // pre[col],前乙個顏色為col的摩天樓的標號
ll f[n][n], // 即所說的f陣列
fac[n][n], // fac(i, j) 用來儲存 i * (i + 1)* … * j
sum[n]; // 即所說的g陣列,這裡省去了一維
inline int ord(char a)
int main()
// 加個哨兵,方便輸出
col[n] = c - 1;
// 把摩天樓的順序調轉
for (int i = 0; i < n >> 1; i ++) swap(col[i], col[n - i - 1]);
for (int i = 0; i <= n + 1; i ++)
for (int j = 0; j <= n + 1; j ++) fac[i][j] = 1ll;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= l + 1; j ++)
pre[col[i]] = i;
sum[i] = (sum[i - 1] * i + f[j - 1][i]) % mod;
} }printf("%i64d\n", f[l + 1][n]);
return 0;
}
AOIP 2015 耶加達的摩天樓
qwq 其實就是個最短路啊,然後發現建的邊最多會有n2 n 2條 所以我們考慮用分塊的思想來優化建圖。pi sqrt n 暴力加入每一條邊,每次最多sqrt n 條邊。pi sqrt n 對於每個點新增sqrt n 個輔助點,這裡可以理解成一棟樓有許多層,每一層一步能走的範圍都不同,然後每一層分別連...
APIO 2015 耶加達的摩天樓
題目鏈結 演算法 考慮將每個 doge 向其所能到達的樓連邊 直接spfa求單源最短路可以獲得57分 那麼 怎樣拿到滿分呢?我們發現這張圖的邊的數量達到了nm的數量級 考慮分塊 將每個點拆成sqrt n 個點 將每個pi sqrt n 的點向 bi pi 連邊 這樣的邊不會超過n sqrt n 條 ...
APIO2015 耶加達的摩天樓
首先可以看出這是一道求最短路的題目,但暴力建圖最多n2 條邊,所以考慮建圖優化 對於p sqrt n 的青蛙可以直接暴力建,最多n sqrt n 條邊 對於p1 define maxn 4000010ul 2 include 3 include 4 include 5 include 6 7usin...