十二省聯考 2019 字串問題 字尾陣列

2022-05-09 16:03:19 字數 3957 閱讀 4491

「十二省聯考 2019」字串問題

題意:給定乙個字串 \(s\),並選取 \(n_a\) 個子串作為 \(a\) 類串,選取 \(n_b\) 個子串作為 \(b\) 類串,還給了 \(m\) 個支配關係:第 \(x\) 個 \(a\) 類串支配第 \(y\) 個 \(b\) 類串。

請你求出最長串 \(t\) 的長度,\(t\) 滿足 \(t = a_+a_+...+a_\),且 \(\forall i\in[1,k-1],a_\) 支配某個串 \(b_q\),而 \(b_q\) 是 \(a_}\) 的字首。

題解:字尾陣列 + 主席樹優化建圖

下面用 \(n\) 表示 \(|s|\),\(\text\) 表示 \(s(x,n)\) 這個字尾。

40 pts

我們可以建出圖論模型:\(a\) 類和 \(b\) 類串都當做點,\(a\) 類串向其支配的 \(b\) 類串連邊,\(b\) 類串向滿足該串是 \(a\) 字首

的 \(a\) 連邊,用 hash 完成這一過程。\(a\) 類點權是其長度,\(b\) 類點權為 \(0\)。先拓撲排序,有環輸出-1,然後按拓撲序 dp 求最長路徑即可。

該演算法缺點在於邊數過大,邊數是 \(o(m + n_a n_b)\) 的。

80 pts

\(o(m)\) 可以接受而 \(o(n_a n_b)\) 不能接受,考慮使用資料結構優化建圖,優化 \(b\) 向 \(a\) 連那部分。

我們考慮若乙個 \(b\) 類串 \(s(l,r)\) 可以連向乙個 \(a\) 類串 \(s(x,y)\),當且僅當 \(\text(\text[l], \text[x]) \geq r - l + 1\) 且 \(y-x+1\geq r-l+1\),即 以 \(a\) 左端點為起點的字尾 和 以 \(b\) 左端點為起點的字尾的 lcp 至少是 \(b\) 的長度,且 \(a\) 的長度不短於 \(b\)。80pts 的測試點滿足 \(|a_i|\geq |b_j|\),第二個條件直接忽略。我們考慮建字尾陣列,這樣 \(b\) 所連的 \(a\) 會在字尾陣列的一段區間中。因此使用線段樹優化建圖,每次二分出區間左右端點,\(b\) 向區間連邊,線段樹底層對應點向 \(a\) 連邊。然後再拓撲 dp。

點數 \(o(n_a+n_b+n)\),邊數 \(o(m + n + n_a + n_b \log n)\)。

100 pts

現在要把長度條件加進去。一看相當於是個矩形連邊,可以使用 kd-tree 優化建圖,不一定能過。

有個還算不錯的做法是把 \(a,b\) 各自按長度從大到小排序,這樣加入 \(b_i\) 之前把 \(|a|\geq |b_i|\) 的都加入資料結構,然後 \(b_i\) 連向資料結構中的乙個區間。很容易發現這個資料結構用主席樹最適合,樹上父子邊直接移到我們的圖里,加入 \(a\) 的時候就建乙個新版本,別忘了新版本對應結點連向舊版本,葉子連向 \(a\) ;\(b\) 連邊就連向表示對應區間的一些結點,這樣 \(b\) 可以走到包括當前版本在內的歷史版本的葉結點。

點數 \(o(n_a \log n + n_b)\),邊數 \(o(m + (n_a + n_b)\log n)\)。空間限制1gb,建議陣列盡量開大。

#include #include #include #define pii pair#define fs first

#define sc second

#define rep(i, j, k) for(int i = j; i <= k; ++ i)

#define per(i, j, k) for(int i = j; i >= k; -- i)

typedef long long ll;

using namespace std;

const int n = 2e5 + 10, nn = n * 30, mm = n * 100;

int n, sa[n], rk[n], cnt[n], t[n];

int h[n], lg[n], st[n][20];

char s[n];

void build()

if(num == n) break ;

}int k = 0;

rep(i, 1, n)

if(k) k --;

int u = sa[rk[i] - 1];

for(; i + k <= n && u + k <= n && s[i + k] == s[u + k]; k ++) ;

h[rk[i]] = k;

}lg[1] = 0;

rep(i, 2, n) lg[i] = lg[i >> 1] + 1;

rep(i, 1, n) st[i][0] = h[i];

rep(j, 1, lg[n]) rep(i, 1, n - (1 << j) + 1)

st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);

}int lcp(int x, int y)

pii search(int x, int len)

l = x; r = n;

while(l <= r)

return ans;

}struct edge e[mm];

int na, nb, rt[n], naid[n], nbid[n], nal[n], nbl[n];

int m, ec, hd[nn], idx, ls[nn], rs[nn];

pii na[n], nb[n];

void clr(int n)

void add(int u, int v) ; hd[u] = ec ++; }

void update(int p, int &u, int l, int r, int x, int a)

int mid = (l + r) >> 1;

if(x <= mid) rs[u] = rs[p], update(ls[p], ls[u], l, mid, x, a);

else ls[u] = ls[p], update(rs[p], rs[u], mid + 1, r, x, a);

if(ls[u]) add(u, ls[u]);

if(rs[u]) add(u, rs[u]);

}void addedge(int u, int l, int r, int ql, int qr, int from)

int mid = (l + r) >> 1;

if(qr <= mid) return addedge(ls[u], l, mid, ql, qr, from);

if(ql > mid) return addedge(rs[u], mid + 1, r, ql, qr, from);

addedge(ls[u], l, mid, ql, mid, from);

addedge(rs[u], mid + 1, r, mid + 1, qr, from);

}int deg[nn];

ll pa[nn];

ll solve()

int cnt = 0;

while(ql < qr) }}

return cnt == idx ? *max_element(pa + 1, pa + idx + 1) : -1;

}bool cmpa(int x, int y)

bool cmpb(int x, int y)

int main()

int pos = 1; rt[0] = 0;

rep(i, 1, nb)

pii p = search(rk[nb[b].fs], nbl[b]);

addedge(rt[pos - 1], 1, n, p.fs, p.sc, na + b);

}printf("%lld\n", solve());

}return 0;

}

十二省聯考 2019 字串問題

以前寫完題後鴿了部落格,現在補一下。今天下午機房的人在大螢幕上一直掛著 ioi 的榜,關注亞塞拜然那邊的比賽情況,我本來是衷心祝願中國隊能有人阿克 day1 的 zzq?然而六點後回來發現只有班傑明阿克了 實際上他還是三小時就阿克了 中國隊最高的是 zzq 和俄羅斯的 300iq 好像是並列第三?可...

十二省聯考2019 字串問題

現有乙個字串 s tiffany 將從中劃出 n a 個子串作為 a 類串,第 i 個 1 leqslant i leqslant n a 為 a i s la i,ra i 類似地,yazid 將劃出 n b 個子串作為 b 類串,第 i 個 1 leqslant i leqslant n b 為...

十二省聯考2019 字串問題

首先,我們可以把題目轉變成這樣 對於一些a類串,其有連向某些b類串的邊 對於某些b類串,其又有連向某些a類串的邊。要你找出一條權值最長的路徑。此時顯然如果成環則答案一定是 1 a到b的串題目已經給出了,關鍵是b到a的串。我們發現,若某個 b 是 a 的字首,則 a 由 b 在後面新增字元得到,需要在...