[ZJOI2015]諸神眷顧的幻想鄉
阿新 • • 發佈:2019-01-12
這題的字串匹配搬到了樹上……?那不在一條鏈咋做啊……不會了,涼涼……
然後麗潔姐姐給我們留了一條生路……就是保證了葉子節點的個數不超過20.
樹上任意一條路徑,我們總能找到一個葉子節點,使得以它為根的時候這條路徑在一條鏈上。那我們可以把每個葉子節點作為根節點來建立廣義字尾自動機,最後直接統計一下不同子串數量就行了。
不過咋建立啊……不會了,涼涼……
建立方法就是找樹上度數為1的點,之後從SAM的根開始建立,這裡有一些不同的是,我們需要返回一下當前的新節點編號,因為我們在樹上遍歷的時候,只知道你要插入的字元,不知道你要往哪插……然後我們就這樣遍歷這棵樹,插入對應字元即可。
最後統計一下不同的子串數量。因為這題是本質不同的個數,所以直接遍歷所有節點,答案加上\(l[i] - l[fa[i]]\)
看一下程式碼。
#include<bits/stdc++.h> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 100005; const int N = 2000005; const ll INF = 5e18; int read() { int ans = 0,op = 1;char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();} while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar(); return ans * op; } int s[M],head[N],ecnt,n,c,x,y,deg[N]; ll ans; struct edge { int next,to,from; }e[N<<1]; struct Suffix { int cnt,ch[N][15],fa[N],l[N]; int extend(int p,int c) { int np = ++cnt; l[np] = l[p] + 1; while(p && !ch[p][c]) ch[p][c] = np,p = fa[p]; if(!p) {fa[np] = 1;return np;} int q = ch[p][c]; if(l[q] == l[p] + 1) fa[np] = q; else { int nq = ++cnt; l[nq] = l[p] + 1,memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq] = fa[q],fa[q] = fa[np] = nq; while(ch[p][c] == q) ch[p][c] = nq,p = fa[p]; } return np; } void cal() { rep(i,2,cnt) ans += l[i] - l[fa[i]]; printf("%lld\n",ans); } }SAM; void add(int x,int y) { e[++ecnt] = (edge){head[x],y,x}; head[x] = ecnt; } void dfs(int x,int fa,int g) { g = SAM.extend(g,s[x]); for(int i = head[x];i;i = e[i].next) if(e[i].to != fa) dfs(e[i].to,x,g); } int main() { n = read(),c = read(),SAM.cnt = 1; rep(i,1,n) s[i] = read(); rep(i,1,n-1) x = read(),y = read(),add(x,y),add(y,x),deg[x]++,deg[y]++; rep(i,1,n) if(deg[i] == 1) dfs(i,0,1); SAM.cal(); return 0; }