點此看題面
大致題意:給定乙個字串,每次詢問給出若干字尾,求兩兩之間的\(lcp\)之和。
明明是想練字尾自動機的,結果看到這題一眼想到字尾陣列,而且一兩分鐘就推出了解法,因此最終默默地寫了字尾陣列。。。
不過真的好坑啊,題目最下面寫了可能存在相同字尾,且相同字尾只計算一次答案。沒看到這句話的我白白浪費了半個小時。
考慮字尾陣列中我們存下了每乙個字尾,而這裡的\(lcp\)正是指某兩個字尾的最長字首,與題目中的意思一致。
回憶一下我們是如何計算\(lcp\),然後就會發現,是依靠\(rmq\)求出一段區間內\(height\)的最小值。
這也正是字尾陣列求\(lcp\)的本質。
考慮這一題,我們按照字尾排序的排名列舉每乙個字尾。
對於乙個字尾,我們只計算排名在它之前的字尾,然後發現它們的\(lcp\)就是一段段右端點相同的區間的最小值之和。
那麼只要開乙個單調遞增的單調棧維護每一段最小值,就可以維護出答案了。
每次只需要加入\(lcp(a_,a_i)\)更新棧即可。
#include#define tp template#define ts template#define reg register
#define ri reg int
#define con const
#define ci con int&
#define i inline
#define w while
#define n 500000
#define ln 20
#define ll long long
#define x 23333333333333333
using namespace std;
int n,m,a[n+5],sp[n+5],sv[n+5];char s[n+5];
class fastio
tp i void read(ty& x)
i void reads(ci n,char *s)
tp i void writeln(ty x)
i void clear()
}f;class suffixarray//字尾陣列
i void getsa(char *s)//求出sa
}i void geth(char *s)//求出height陣列
for(lg[0]=-1,i=1;i<=n;++i) lg[i]=lg[i>>1]+1;//rmq預處理
for(j=1;j<=lg[n];++j) for(i=1;i+(1<=x&&(x-=x))
#define dec(x,y) ((x-=(y))<0&&(x+=x))
t=s.lcp(a[i-1],a[i]);w(t&&sv[t]>=t) dec(res,1ll*sv[t]*(sp[t]-sp[t-1])),--t;//維護棧的單調性,彈出元素時要更新答案
sv[++t]=t,sp[t]=i,inc(res,1ll*sv[t]*(sp[t]-sp[t-1])),inc(ans,res);//把lcp加入棧中,累加答案
}f.writeln(ans);
}return f.clear(),0;
}
bzoj3879 SvT 字尾陣列 RMQ
題目大意 給定乙個字串。每次詢問給定 t 個位置,求兩兩位置開頭的字尾的 lcp 之和。注釋 1 le length le 5 cdot 10 5 sum t le 3 cdot 10 6 想法 不難想到構建字尾陣列。進而我們的問題就轉化成了給定序列上一些位置求這些位置兩兩之間區間最小值的和。對 h...
BZOJ 3879 SvT 虛樹 字尾樹
傳送門 題意 多次詢問,給出一些字尾,求兩兩之間 lcp 之和 哈哈哈哈哈哈哈竟然 1a 了,剛才還在想如果寫不好這道題下節數學就不上了,看來是上天讓我上數學課啊 suffix virtual tree 沒有多次詢問就是那道差異了 多次詢問總次數 o n 建出字尾樹每次建虛樹就行了 然後詢問給出的是...
BZOJ3879 SvT(字尾自動機,虛樹)
bzoj 看著這個東西,詢問若干個字首兩兩之間的 lcp 顯然 lcp 就是 sam 構建出來的 parent 數上的 lca 所代表的長度。那麼這樣子就轉為了樹型 dp 然後發現是字首?把串轉過來就是字尾了。sum t 是 o n 級別的?顯然虛樹。那麼直接虛樹搞搞就好了。include incl...