看到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 時間回答 均攤這兩種操作,可以這樣做 由於只可以從前往後跳,所以可以把跳躍路徑壓縮,更新時把壓縮的部...