zrq成功從坍塌的洞穴中逃了出來。終於,他看到了要研究的礦石。他想挑一些帶回去完成任務。
zrq發現這裡有 \(n\) 塊排成一排的礦石。
他用乙個小寫字母來表示每塊礦石,他還發現每塊礦石有乙個重要度 \(v_i\)。
zrq想採集一段連續的礦石回研究所。
他非常嚴格,被採集的一段礦石必須滿足小寫字母的字典序降序排名等於這段礦石的重要度和。
這裡多個出現在不同位置的本質相同串的字典序排名相同。
比如說字母串為aa
,那麼第乙個a
的排名和第二個a
的排名相同,都是2
(第1
是aa
)。
zrq問你,在原串中有哪些不同的子串可以被採集?
這裡子串不同定義為出現位置不同,也就是說本質相同的子串出現在不同位置都要計算一次(當然重要度和等於排名是前提)。
比如共有 \(4\) 塊礦石,小寫字母串為abcd
,重要度各為10 0 1 1
。
我們把所有的子串按照字典序從大到小排名:1:d 2:cd 3:c 4:bcd 5:bc 6:b 7:abcd 8:abc 9:ab 10:a
。
那麼串d
的排名為 \(1\)(第一大),重要度和為 \(1\),可以被採集。
串cd
的排名為 \(2\),重要度和為 \(2\),可以被採集。
串a
的排名為 \(10\),重要度和為 \(10\),可以被採集。
其他串則不滿足這個條件,故有三個串可以被採集。
第一行乙個長度為 \(n\) 由小寫字母組成的字串,每個字元代表乙個礦石。
第二行 \(n\) 個整數,表示 \(v_i\)。
一行乙個整數,表示能被採集的子串個數 \(s\)。
接下來 \(s\) 行每行兩個整數 \(l,r\),分別表示每個可採集子串的左端點與右端點,按照左端點公升序為第一關鍵字,右端點公升序為第二關鍵字排序。
輸入 #1
abcd
10 0 1 1
輸出 #1
3
1 13 4
4 4
輸入 #2
aaaa
1 1 1 1
輸出 #2
0
輸入 #3
aaa
1 1 1
輸出 #3
2
1 22 3
輸入 #4
aaa
1 1 2
輸出 #4
1
1 2
共 \(10\) 個測試點,每個點 \(10\) 分,計 \(100\) 分。
對於所有測試點,有 \(n\leq 10^5\),\(0 \le v_i \le 1000\)。保證每個點可被採集的子串不超過 \(10^5\) 個。
樣例#1解釋放在題面裡了。
樣例#2解釋:
每個子串都不滿足條件。
串a
的排名是 \(4\),重要度和都是 \(1\)。
串aa
的排名是 \(3\),重要度和都是 \(2\)。
串aaa
的排名是 \(2\),重要度和都是 \(3\)。
串aaaa
的排名是 \(1\),重要度和都是 \(4\)。
樣例 #3解釋:
串a
的排名是 \(3\),重要度和都是 \(1\)。
串aa
的排名是 \(2\),重要度和都是 \(2\),共有兩個串aa
,位置分別為 \(1 \sim 2\) 和 \(2 \sim3\)。
串aaa
的排名是 \(1\),重要度和都是 \(3\)。
樣例 #4解釋:
可以發現,串 \(2 \sim 3\)(第二個aa
)不滿足條件了。它的排名還是 \(2\) 不變,但是重要度和為 \(3\)。
前置知識:字尾陣列
要求出所有排名等於重要度的子串並輸出方案
首先可以證明這樣的子串不會超過 \(n\) 個
因為假如我們固定了左端點,那麼隨著右端點的增大,重要度不會變小,但是排名會變小
因此對於乙個確定的左端點,最多隻會有乙個右端點滿足條件
又因為重要度和排名都是單調的,所以只需要固定左端點,二分合法的右端點就可以了
考慮如何快速求出乙個子串的重要度和排名
重要度可以用字首和 \(o(1)\) 查詢
排名可以用字尾陣列求出
在計算乙個字串的本質不同的子串時,我們會用到乙個式子
\(\sum_^nn-sa[i]+1-height[i]\)
乙個很有用的性質就是這樣求出的本質不同字串是按順序的
所以對於乙個子串,只需要算出它的前面有多少本質不同的子串即可
先用乙個陣列記錄一下這個式子的字首和,然後分情況討論
設 \(fir[i]\) 為 \(i\) 號字尾的排名
如果 \(len \geq height[fir[l]]\),那麼排名為 \(sum_-(n-r)\)
否則向前二分找到乙個位置 \(pos\),使得它恰好滿足 \(len \geq height[pos]\),那麼排名為 \((sum_-(n-sa[pos]-len+1))\)
後面減去的那一部分是左端點相同但是長度比當前子串長的
因為排名是按照字典序從大到小來的,所以還要拿總的本質不同的子串減去求出的結果
時間複雜度 \(o(nlog^2n)\)
#include#include#include#include#include#define rg register
inline int read()
while(ch>='0' && ch<='9')
return x*fh;
} const int maxn=1e5+5;
int sa[maxn],fir[maxn],sec[maxn],tax[maxn],n,heig[maxn],lg[maxn],mmin[maxn][20],m;
int a[maxn],sum1[maxn],sta1[maxn],sta2[maxn],tp;
char s[maxn];
long long tot,sum2[maxn];
void qsort()
void getsa()
}void getheight()
tot=1ll*n*(n+1)/2ll;
for(rg int i=2;i<=n;i++) tot-=heig[i];
for(rg int i=1;i<=n;i++) sum2[i]=sum2[i-1]+n-sa[i]+1-heig[i];
}void pre()
return tot-(sum2[nr]-(n-sa[nr]-len+1))+1;
}void solve(rg int id) else if(getrk(id,mids)>getval(id,mids)) else }}
int main()
題解 洛谷 P4143 採集礦石
對於乙個固定的左端點,右端點向右移動時,其子串權值和不斷增大,字典序降序排名不斷減小,因此對於乙個左端點,最多存在乙個右端點使其滿足條件。所以可以列舉左端點,然後二分右端點的位置,權值和通過字首和來查詢,現在的問題就是如何快速查詢乙個子串的排名。考慮用字尾陣列來解決,對於乙個子串 l,r 對於在位置...
洛谷P3181 字尾陣列
題目要求求出兩個兩個字串中相同子串的方案數,那麼我們將其拼接起來,去求出拼接後的字串中含有相同子串的數量。當然這樣做會求出同乙個字串中相同子串的數量,所以我們還需要如法炮製分別求出兩個字串中的答案,然後用總貢獻減去他們。那麼問題就變成了如何求出乙個字串中相同子串的數量。實際上這就是求任意兩個字尾x,...
洛谷 P3809 模板 字尾排序 字尾陣列
題目描述 讀入乙個長度為 n n 的由大小寫英文本母或數字組成的字串,請把這個字串的所有非空字尾按字典序從小到大排序,然後按順序輸出字尾的第乙個字元在原串中的位置。位置編號為 1 1 到 n n 輸入輸出格式 輸入格式 一行乙個長度為 n n 的僅包含大小寫英文本母或數字的字串。輸出格式 一行,共n...