HNOI2010 公交線路

2022-04-26 01:12:06 字數 1384 閱讀 3116

看到k,p的範圍這麼小,顯然要狀壓dp啊!

但是要怎麼狀壓dp呢。

我們先注意到每p個公交站,這k輛公交車都要至少出現一次。因為答案是按集合算的,所以公交車之間不做區別,換句話說就是我們可以講題目簡化一下——

1-n的n個元素,k個集合,保證乙個元素只出現在乙個集合中(不能多餘乙個也不能少於乙個),任意乙個集合中的元素從小到大排序之後,兩兩之間的編號相差不能超過p,問方案數?

我們設狀態\(dp[i][j]\)表示前i個數,從i(包括i)往前p個數的狀態,方案數。因為要狀壓,所以肯定要想出來如何用01表示狀態——0表示這個位置不是任何乙個集合當前選的最後乙個數,1表示是。

那麼就是每p個數中,一定要有k個數是1。

我們預處理出來合法的狀態,然後再處理出哪些狀態之間可以互相轉移——i可以轉移到j,當且僅當\((i<<1)\&j==k-1\)(j因為最後一位必須為1)。這樣算起來第二維狀態數最多也是\(2^\),不是很大。

然後就是\(dp[i][j]=\sum dp[i-1][k]\),其中k狀態能夠轉移到j。

發現n很大,所以要矩陣快速冪。

具體實現可以看一下**。

最後額外解釋一下:

1、為什麼矩陣快速冪的次數為n-k?因為開始k個是出發站,所以每一位一定是1。我們從\([k+1,k+p]\)開始,而且它們肯定都能從k個1轉移過來。

2、為什麼最後輸出的是[1][1]?因為矩陣快速冪裡面記錄的都是預處理出來的合法狀態,而我們處理出來的第乙個合法狀態是0011...11(一共p位,後k位為1),考慮一下位運算的順序,這是很顯然的。題目中要求的也是最後k為必須為終點站,所以我們直接輸出第乙個的答案就行了。

#include#include#include#include#include#define maxn 155

#define mod 30031

using namespace std;

int n,k,p,cnt;

int c[maxn];

struct nodea,init;

inline node calc(node x,node y)

inline node fpow(node x,int y)

return cur;

}inline bool check(int x,int y)//x是否能夠轉移到y

int main()

for(int i=1;i<=cnt;i++)

for(int j=1;j<=cnt;j++)

if(check(i,j)&&(c[j]&1))

init.t[i][j]=1;

init=fpow(init,n-k);

printf("%d\n",init.t[1][1]);

return 0;

}

HNOI2010 公交線路

狀壓 矩乘的好題。因為每 p 個位置中,每輛車就至少有 1 個位置,所以我們可以狀壓一下。設 f i j 表示 區間 i,i p 1 內的車站現在的規劃情況是 j 的方案數。顯然,必有 j 的第 p 位是 1 且 j 共有 k 位是 1 j 的第 p 位對應著 i 則 f i j sum f i 1...

HNOI2010 公交線路 bus

標籤 狀態壓縮 矩陣快速冪。題解 首先看範圍,p 10,那麼我們可以想到狀態壓縮。我們把從乙個長度為10的區間進行壓縮,1代表可以,那麼當值乙個區間的1的個數為k個,我們就認為他是合法的。要注意這裡所定義的區間,是有起點的,但是沒有記下來,因為沒有必要。然後我們就可以想一下狀態是怎麼轉移的。那麼可行...

hnoi2010 彈飛綿羊

題目描述很明確,現在的目標是均攤兩個操作的複雜度 現在我們已知有兩種方法 1.每次用o 1 的時間修改k值,用o n 的時間直接模擬回答詢問 2.每次修改了k值後用o n 的時間更新所有答案,o 1 時間回答 均攤這兩種操作,可以這樣做 由於只可以從前往後跳,所以可以把跳躍路徑壓縮,更新時把壓縮的部...