P3294 [SCOI2016]背單詞
阿新 • • 發佈:2018-09-09
eof lba getchar() hold ref mem 關於 pla algo
P3294 [SCOI2016]背單詞
Trie+貪心
倒插進樹+取出重建+子樹處理+貪心遍歷
倒插進樹:把後綴轉化為前綴,所以把字符串倒著插進Trie中
取出重建:重新建立一棵以單詞為節點的樹,如果存在包含(前綴)關系就連邊
子樹處理:處理出每棵樹的大小用於貪心
貪心遍歷:遍歷整棵新樹,累加答案
關於貪心:每次找到最小的子樹統計答案
end.
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> usingnamespace std; struct data{ int nxt[27],end; data(){memset(nxt,0,sizeof(nxt)); end=0;} }a[510003]; vector <int> g[100002]; //存邊 int n,cnt,len,siz[100002]; long long f[100002]; char q[510003]; inline bool cmp(const int &A,const int &B) {return siz[A]<siz[B];} inline void read_q(){ char c=getchar(); len=0; while(c<‘a‘||c>‘z‘) c=getchar(); while(‘a‘<=c&&c<=‘z‘) q[len++]=c,c=getchar(); } inline void insert_(int id){ //建樹 read_q(); int u=0; for(int i=len-1;i>=0;--i){ int p=q[i]-‘a‘; if(!a[u].nxt[p]) a[u].nxt[p]=++cnt; u=a[u].nxt[p]; }a[u].end=id; //編號代替單詞 } inline void rebuild(int x,int p){ //重新建樹 if(a[x].end) g[p].push_back(a[x].end); //存在前綴關系連邊 for(int i=0;i<26;++i){ int to=a[x].nxt[i]; if(!to) continue; rebuild(to,a[x].end ? a[x].end:p); } } inline void dfs1(int x){ siz[x]=1; for(int i=0;i<g[x].size();++i){ int to=g[x][i]; dfs1(to); siz[x]+=siz[to]; } sort(g[x].begin(),g[x].end(),cmp); //按子樹從小到大排序 } inline void dfs2(int x){ f[x]=x? 1:0; long long tmp=0; for(int i=0;i<g[x].size();++i){ int to=g[x][i]; dfs2(to); f[x]+=f[to]+tmp; //貪心累加答案 tmp+=siz[to]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) insert_(i); rebuild(0,0); dfs1(0); dfs2(0); printf("%lld",f[0]); return 0; }
P3294 [SCOI2016]背單詞