對於乙個固定的左端點,右端點向右移動時,其子串權值和不斷增大,字典序降序排名不斷減小,因此對於乙個左端點,最多存在乙個右端點使其滿足條件。
所以可以列舉左端點,然後二分右端點的位置,權值和通過字首和來查詢,現在的問題就是如何快速查詢乙個子串的排名。
考慮用字尾陣列來解決,對於乙個子串\([l,r]\),對於在位置\(l\)對應的字尾排名之前的字尾中的子串是能對該子串的排名產生貢獻的。
若該子串的長度比\(l\)對應的字尾和前乙個字尾的\(lcp\)大,即\(len>ht_\),也就是該子串沒有被\(lcp\)所包括,則其排名為\(sum_l-(n-l+1-len)\),其中\(sum_l\)表示\(l\)所對應的字尾之前所有的字尾中本質不同的子串個數,然後再減去右端點\(r\)右邊多算的部分,即為該子串的排名。然後求降序排名時,用本質不同子串個數減去正序排名即可。
若該子串被\(lcp\)所包括,那麼直接像上面那樣計算是不對的,則需向前二分到第乙個和\(l\)所對應的字尾的\(lcp\)恰好等於\(len\)的位置,然後和上面一樣用該位置計算排名。
最終的複雜度為\(o(n\ log^2\ n)\)
\(code:\)
#include#define maxn 400010
#define mk make_pair
using namespace std;
typedef long long ll;
templateinline void read(t &x)
while(isdigit(c))
if(flag)x=-x;
}ll n,m,ans,tot;
int v[maxn],b[maxn],rk[maxn],sa[maxn],tp[maxn],ht[maxn];
int lg[maxn],f[maxn][25];
ll sum[maxn];
char s[maxn];
vector> ve;
void rsort()
void sa()
int k=0;
for(int i=1;i<=n;++i) rk[sa[i]]=i;
for(int i=1;i<=n;++i)
tot=n*(n+1)/2;
for(int i=1;i<=n;++i) tot-=ht[i];
sum[sa[1]]=n-sa[1]+1;
for(int i=2;i<=n;++i)
sum[sa[i]]=sum[sa[i-1]]+n-sa[i]+1-ht[i];
}void init()
return tot-(sum[sa[p]]-(n-sa[p]+1-len))+1;
}}void work()
else if(rank>val) l=mid+1;
else r=mid-1;}}
}int main()
洛谷 P4143 採集礦石 字尾陣列
zrq成功從坍塌的洞穴中逃了出來。終於,他看到了要研究的礦石。他想挑一些帶回去完成任務。zrq發現這裡有 n 塊排成一排的礦石。他用乙個小寫字母來表示每塊礦石,他還發現每塊礦石有乙個重要度 v i zrq想採集一段連續的礦石回研究所。他非常嚴格,被採集的一段礦石必須滿足小寫字母的字典序降序排名等於這...
洛谷 P1447 能量採集
此題雖為紫,但其實在水 能量採集 棟棟有一塊長方形的地,他在地上種了一種能量植物,這種植物可以採集太陽光的能量。在這些植物採集能量後,棟棟再使用乙個能量匯集機器把這些植物採集到的能量匯集到一起。棟棟的植物種得非常整齊,一共有 nn 列,每列有 mm 棵,植物的橫豎間距都一樣,因此對於每一棵植物,棟棟...
洛谷 P1101 題解
這道題可以用深搜 回溯 來寫,相信大部分人都是這麼想的,但是有些人可能在一些地方饒了半天,所以這裡就貼一下我的思路,個人覺得自己的很好懂,除了tx和ty那裡,但是tx和ty的那種用法對於輸出路徑的題目一般很實用 這個算是比較簡單的吧,題目裡給出了具體要找的字串,我們要做的就是對它進行8個方向的搜尋,...