BZOJ 4516 Sdoi2016 生成魔咒

2022-03-18 04:40:16 字數 1417 閱讀 5460

題鏈:

題解:

把串反過來後,問題變為求每個字尾的互不相同的子串個數。

首先用倍增演算法求出 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 的非空字...