「十二省聯考 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 在後面新增字元得到,需要在...