BJOI2017 魔法咒語

2022-04-29 06:00:07 字數 3058 閱讀 5364

題目鏈結

給 \(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號兒子 顏色的影響,可以記錄每段的路徑權值 邊數以及該段的介面,將所有的路徑以介面顏色為第一關鍵字,介面編號為第二關鍵字排序。顯然,對於同一介面的路...