NOI2016 優秀的拆分

2022-03-03 03:41:53 字數 3711 閱讀 7611

如果乙個字串可以被拆分為 $aabb$ 的形式,其中 $a$ 和 $b$ 是任意非空字串,則我們稱該字串的這種拆分是優秀的。

例如,對於字串 aabaabaa,如果令 $a = \mathrm$,$b = \mathrm$,我們就找到了這個字串拆分成 $aabb$ 的一種方式。

乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 $a=\mathrm$,$b=\mathrm$,也可以用 $aabb$ 表示出上述字串;但是,字串 abaabaa 就沒有優秀的拆分。

現在給出乙個長度為 $n$ 的字串 $s$,我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這裡的子串是指字串中連續的一段。

以下事項需要注意:

出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。

在乙個拆分中,允許出現 $a=b$。例如 cccc 存在拆分 $a=b=\mathtt$。

字串本身也是它的乙個子串。

每個輸入檔案包含多組資料。輸入檔案的第一行只有乙個整數 $t$,表示資料的組數。保證 $1 \le t \le 10$。

接下來 $t$ 行,每行包含乙個僅由英文小寫字母構成的字串 $s$,意義如題所述。

輸出 $t$ 行,每行包含乙個整數,表示字串 $s$ 所有子串的所有拆分中,總共有多少個是優秀的拆分。

對於全部的測試點,保證 $1 \le t \le 10$。以下對資料的限制均是對於單組輸入資料而言的,也就是說同乙個測試點下的 $t$ 組資料均滿足限制條件。

我們假定 $n$ 為字串 $s$ 的長度,每個測試點的詳細資料範圍見下表:

測試點編號

$n$其他約束

1、2$\leq 300$

$s$中所有字元全部相同

3、4$\leq 2000$

5、6$\leq 10$

無7、8

$\leq 20$

9、10

$\leq 30$

11、12

$\leq 50$

13、14

$\leq 100$

15$\leq 200$

16$\leq 300$

17$\leq 500$

18$\leq 1000$

19$\leq 2000$

20$\leq 30000$

似乎暴力就有95分啊?

先\(o(n^2)\)預處理雙hash,用來判斷子串是否相同

然後\(o(n^2)\)處理\(f[i]\),\(f[i]\)表示結尾位置為\(i\),滿足\(aa\)的子串數量

然後可以直接根據\(f[i]\)在\(o(n^2)\)的的時間內得到\(g[i]\),\(g[i]\)表示,結尾為i滿足\(aabb\)的子串數量

很基礎啊?

#include#include#include#include#include#include#include#include#include#include#define ll long long

using namespace std;

inline char nc()

return *p1++;}

inline void read(int &x)

inline void read(ll &x)

inline int read(char *s)

inline void read(char &x)

int wt,ss[19];

inline void print(int x)

}inline void print(ll x)

}int t,n,f1[2010][2010],f2[2010][2010],f[2010],g[2010];

char s[2010];

const int mo1=100271,mo2=500179;

void init()

}int main()

} for (int i=3;i<=n;i++)

}int ans=0;

for (int i=4;i<=n;i++)

ans+=g[i];

print(ans),puts("");

} return 0;

}

當時考場上沒打算為了這5分再去思考啊

不過正解的思想還是很不錯的

基於上面的思想,我們可以看到$$ans=\sum_^^ \frac=nlogn\)

現在還有乙個問題是怎麼求\(a,b\)的最長公共字首\(p\)和最長公共字尾\(s\)

首先可以看一下uoj35,他所求的是相鄰\(rank\)的lcp

我們假設\(h[i]=lcp\\)

可以得到對於任意的\(j\)和\(k\)(假設\(rank[j]),\(lcp\=min\\)

直接用st預處理,每次詢問都是\(o(1)\),對上述複雜度無影響

ps.注意,我的**使用sam來構造sa的,由於sam建立的時候會有新的節點產生,所以陣列需要開大一些,不然會gg

然後最後的乙份問題就是要進行區間加1的操作,直接差分即可,不要再往複雜度上加無謂的log

#include#include#include#include#include#include#include#include#include#include#define ll long long

using namespace std;

inline char nc()

return *p1++;}

inline void read(int &x)

inline void read(ll &x)

inline int read(char *s)

inline void read(char &x)

int wt,ss[19];

inline void print(int x)

}inline void print(ll x)

}int n,m,s,b[80010],c[80010],d[80010],f[80010],g[80010];

char sx[80010];

struct data

a[80010];

int sa[80010],rank[80010],r1[80010],r2[80010],rank,f1[80010][20],f2[80010][20];

void extend(int x,int p)

int np=a[p].letter[x];

if (a[np].len==a[p].len+1) a[q].fa=np;

else

}void insert(char x)

void dfs(int x)

void build()

rank=0;

dfs(1);

}ll query(int x,int y)

void swap(char *s)

x+=i,y+=i;

}} int f=0,g=0;

for (int i=1;i<=m;i++)

f+=f[i],f[i]=f,g+=g[i],g[i]=g;

ll ans=0;

for (int i=1;ians+=(ll)g[i]*f[i+1];

print(ans),puts("");

} return 0;

}

NOI2016 優秀的拆分

看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...

NOI2016 優秀的拆分

題目實際上要求我們求從每個點出發的aa串的數量 考慮點i的答案,發現如果字首i與字首j j i 的最長公共字尾 i j,那麼i點出發向前就存在乙個長度為i j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件 考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的...

NOI2016 優秀的拆分

點此看題 首先轉化問題,我們可以求出a i b i a i b i a i b i 即以i ii結束 開始的aaaa aa串的數量,這樣答案就可以表示為 a i b i 1 sum a i times b i 1 a i b i 1 求這兩個陣列,可以隔距離len lenle n設定乙個點,這樣乙個...