題目鏈結
給 \(n\) 個基本詞彙, \(m\) 個禁忌詞語。求用基本詞彙(每個詞彙可重複詞彙)拼成長度為 \(l\) 的
不包含任何禁忌詞語的字串的方案數。
在 資料規模與約定 中,我們發現可以把資料劃分成兩檔:
\(l \le 100\) 的(前 \(60pts\)) 。
基本長度不超過 \(2\) 的
顯然不包含這個東西判定可以用 ac 自動機,用 \(m\) 個禁忌詞語建 ac 自動機,把非法點標記一下(即所有詞語末尾及其在 fail 樹上的子樹)。
然後方案數這個東西顯然的樸素 dp:
考慮轉移就是拼接乙個基本詞彙,假設目前在 \(f[i][u]\),設詞彙長度為 \(l\),就讓這個詞彙從 \(u\) 出發在 ac 自動機上跑全串,然後落在 \(v\) 節點。要求這一段路徑上都不能走非法點才可以轉移。這樣 \(f[i][u]\) 就對 \(f[i + l][v]\) 有了加的貢獻。
每次轉移這個過程可以先用 \(o(100 ^ 2n)\)列舉 \(u\) 和基本詞彙來確定是否為合法轉移和轉移到的圖上節點。
然後 dp 複雜度是 \(o(100nl)\) 的,可以跑過。
下面 $60pts $ 的**
#include #include #include using namespace std;
const int n = 55, s = 105, p = 1e9 + 7;
int n, m, l, q[s], g[s][n], f[s][s], len[n];
int idx = 0, tr[s][26], fail[s];
bool e[s];
char a[n][s], b[n][s];
void insert(char s)
e[p] = true;
}void build() else tr[u][i] = tr[fail[u]][i];
} }}void work(int u, int x)
for (int i = 0; i < len[x]; i++)
} g[u][x] = p;
}void solve1()
}} }
int ans = 0;
for (int i = 0; i <= idx; i++)
if (!e[i]) (ans += f[l][i]) %= p;
printf("%d\n", ans);
}int main()
build();
solve1();
return 0;
}
看到 \(l \le 10^8\),我們的複雜度顯然不能跟 \(l\) 相關。
先考慮長度都是 \(1\) 的時候,那麼轉移過程中,\(f[i]\) 這層只會對 \(f[i + 1]\) 這層產生貢獻,而且每次產生貢獻的方式是相同的、且是加和貢獻,這個東西我們是會用矩陣乘法優化的,即用每一層作為乙個矩陣,每一次迭代計算下一層結果,類似 hnoi2008 gt考試。
接著用相同的思想考慮長度 $ \le 2$ 的情況,那麼 \(f[i]\) 這層只會對 \(f[i + 1], f[i + 2]\) 產生影響,這種情況仍可以用矩陣乘法優化,即每乙個矩陣記錄兩層資料,但是要更複雜一點。
設計矩陣為 \([f_, f_,...,f_, f_, f_, ...,f_ ]\)
考慮把 \([f_i, f_] \times a = [f_, f_]\),構造乙個矩陣 \(a\)。
一些細節問題:
這個時間複雜度是 \(o(100^3logl)\) 的。
#include #include #include using namespace std;
typedef long long ll;
const int n = 55, s = 105, p = 1e9 + 7;
int n, m, l, q[s], g[s][n], f[s][s], len[n];
int idx = 0, tr[s][26], fail[s];
bool e[s];
char a[n][s], b[n][s];
// 矩陣
struct matrix
} a, res;
// ac 自動機
void insert(char s)
e[p] = true;
}void build() else tr[u][i] = tr[fail[u]][i];
} }}// 預處理轉移方式
void work(int u, int x)
for (int i = 0; i < len[x]; i++)
} g[u][x] = p;
}// 前 60 pts
void solve1()
}} }
int ans = 0;
for (int i = 0; i <= idx; i++)
if (!e[i]) (ans += f[l][i]) %= p;
printf("%d\n", ans);
}// 獲取 id
int num(int id, int c)
// 後 40 pts
void solve2()
} // 構造 a
for (int u = 0; u <= idx; u++)
} }int b = l;
while (b)
int ans = 0;
for (int i = 0; i <= idx; i++)
if (!e[i]) (ans += res.w[1][i + 1]) %= p;
printf("%d\n", ans);
}int main()
build();
for (int u = 0; u <= idx; u++)
for (int i = 1; i <= n; i++) work(u, i);
if (l <= 100) solve1();
else solve2();
return 0;
}
BJOI2017 魔法咒語
矩陣乘法 ac 自動機 是道很不錯的題了 首先是前六十分,就是乙個 ac 自動機上的套路 dp 設 dp i j 表示匹配出的長度為 i 在自動機上位置為 j 的方案數,轉移的話就列舉下乙個單詞選擇哪個放到自動機上一波匹配就好了 後面 40 分強行變成了另外一道題,l 變成了 1e8 一看就是矩乘的...
BJOI2017 機動訓練
落谷 loj。定義機動路徑為 相同地形序列指路徑上順序經過的地形序列。定義機動路徑的權值為相同地形序列的數量之和。求所有機動路徑的權值之和。同一類機動路徑,他的貢獻就是數量的平方 leftrightarrow 答案即本質不同機動路徑數量的平方和 leftrightarrow 即兩個人走的機動路徑形式...
BJOI2017 樹的難題
按照常規思路,選乙個點x作為分治中心,拼接x出發到子樹各點的路徑。對於拼接時兩段介面處 即x連出的那條邊,若沒有,設為0號邊 顏色為0,長度為0,到達0號兒子 顏色的影響,可以記錄每段的路徑權值 邊數以及該段的介面,將所有的路徑以介面顏色為第一關鍵字,介面編號為第二關鍵字排序。顯然,對於同一介面的路...