【題解】P4070 [SDOI2016]生成魔咒(SAM/字尾自動機)
阿新 • • 發佈:2022-12-12
[SDOI2016]生成魔咒
題目描述
魔咒串由許多魔咒字元組成,魔咒字元可以用數字表示。例如可以將魔咒字元 \(1,2\) 拼湊起來形成一個魔咒串 \([1,2]\)。
一個魔咒串 \(S\) 的非空字串被稱為魔咒串 \(S\) 的生成魔咒。
例如 \(S=[1,2,1]\) 時,它的生成魔咒有 \([1],[2],[1,2],[2,1],[1,2,1]\) 五種。\(S=[1,1,1]\) 時,它的生成魔咒有 \([1],[1,1],[1,1,1]\) 三種,最初 S 為空串。
共進行 \(n\) 次操作,每次操作是在 \(S\) 的結尾加入一個魔咒字元。每次操作後都需要求出,當前的魔咒串 \(S\)
輸入格式
第一行一個整數 \(n\)。
第二行 \(n\) 個數,第 \(i\) 個數表示第 \(i\) 次操作加入的魔咒字元 \(x_i\)。
輸出格式
輸出 \(n\) 行,每行一個數。
第 \(i\) 行的數表示第 \(i\) 次操作後 \(S\) 的生成魔咒數量
樣例 #1
樣例輸入 #1
7
1 2 3 3 3 1 2
樣例輸出 #1
1
3
6
9
12
17
22
提示
資料規模與約定
對於 \(10\%\) 的資料,保證 \(1 \le n \le 10\);
對於 \(30\%\) 的資料,保證 \(1 \le n \le 100\);
對於 \(60\%\)
對於 \(100\%\) 的資料,保證 \(1 \le n \le 10^5\),\(1 \leq x_i \leq 10^9\)。
題解
考慮SAM中的link指向的是和它endpos不同的最長字尾,新加點的長度減去它的link的長度即增加的長度,直接建SAM即可。
#include<bits/stdc++.h> using namespace std; inline int rd(){ int f=1,j=0; char w=getchar(); while(!isdigit(w)){ if(w=='-')f=-1; w=getchar(); } while(isdigit(w)){ j=j*10+w-'0'; w=getchar(); } return f*j; } const int N=100001; struct node{ unordered_map<int,int>to; int len,fro; }sam[N*2]; int n,last,cnt; long long ans; void insert(int c){ int p=last,now=++cnt; sam[now].len=sam[p].len+1; while(p&&(!sam[p].to[c]))sam[p].to[c]=now,p=sam[p].fro; last=now; if(!p)return sam[now].fro=1,void(0); if(sam[p].len+1==sam[sam[p].to[c]].len)return sam[now].fro=sam[p].to[c],void(0); int clone=++cnt,qnode=sam[p].to[c]; sam[clone]=sam[qnode]; sam[clone].len=sam[p].len+1; sam[qnode].fro=sam[now].fro=clone; while(p&&sam[p].to[c]==qnode)sam[p].to[c]=clone,p=sam[p].fro; return ; } signed main(){ n=rd(); last=cnt=1; for(int i=1;i<=n;i++){ int x=rd(); insert(x); ans+=sam[last].len-sam[sam[last].fro].len; printf("%lld\n",ans); } return 0; }