題鏈:
題解:
把串反過來後,問題變為求每個字尾的互不相同的子串個數。
首先用倍增演算法求出 sa,rank,height,然後對 height陣列建立 st表。
接著求出整個串的子串個數,ans+=n-sa[i]-height[i]。(我從0開始編號的)
式子的含義就是考慮每個字尾相比它的前一名,多了幾個與之前不同的且串頭為該字尾的頭的子串。
(一定要清晰地懂得並理解那個式子哦)
之前得出了0 位置開始的字尾(即整個串)的子串個數,
那麼現在就需要把 rank[0]這個字尾從排好序的字尾陣列中去除。
然後維護出新的字尾(即從1位置開始的字尾)的子串個數。
怎麼做呢,反向考慮 ans的求法:
即把rank[0]產生的貢獻減去(包括和它上面一名以及和它下面一名產生的貢獻),相當於該字尾被去除了。
這時排在rank[0]上面一位的字尾(設為 u),和排在rank[0]下面一位的字尾(設為 d),
就挨在了一起,那麼要加上 u 字尾和 d 字尾的貢獻。
然後就得到了新的字尾的子串個數。
之後的其它字尾的計算就類似了。
另外再提一下,在找當前字尾的上一名字尾和下一名字尾時,找到的必須是還在字尾陣列中(即還沒有被去除),
可以用類似並查集的思想維護(好吧,是路徑壓縮的思想),做到均攤 o(1)。
除開倍增演算法和求st表的複雜度 o(n)
**:
#include#include#include#include#define maxn 100500#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
int sa[maxn],rak[maxn],hei[maxn];
int up[maxn],down[maxn],a[maxn],log2[maxn],stm[maxn][20];
bool vis[maxn];
void build(int n,int m)
for(int i=0;ir) swap(l,r); l++;
k=log2[r-l+1];
return min(stm[l+(1<>1]+1;
int n,cnt; scanf("%d",&n);
for(int i=n-1;i>=0;i--) scanf("%d",&a[i]),tmp[i]=a[i];
sort(tmp,tmp+n);
cnt=unique(tmp,tmp+n)-tmp;
for(int i=0;i
bzoj4516 SDOI2016 生成魔咒
time limit 10 sec memory limit 128 mb submit 376 solved 232 submit status discuss 魔咒串由許多魔咒字元組成,魔咒字元可以用數字表示。例如可以將魔咒字元 1 2 拼湊起來形成乙個魔咒串 1,2 乙個魔咒串 s 的非空字串...
bzoj4516 Sdoi2016 生成魔咒
4516 sdoi2016 生成魔咒 time limit 10 sec memory limit 128 mb submit 575 solved 327 submit status discuss 魔咒串由許多魔咒字元組成,魔咒字元可以用數字表示。例如可以將魔咒字元 1 2 拼湊起來形成乙個魔咒...
bzoj 4516 Sdoi2016 生成魔咒
time limit 10 sec memory limit 128 mb submit 1026 solved 576 submit status discuss 魔咒串由許多魔咒字元組成,魔咒字元可以用數字表示。例如可以將魔咒字元 1 2 拼湊起來形成乙個魔咒串 1,2 乙個魔咒串 s 的非空字...