1. 程式人生 > >P3294 [SCOI2016]背單詞

P3294 [SCOI2016]背單詞

eof lba getchar() hold ref mem 關於 pla algo

P3294 [SCOI2016]背單詞

Trie+貪心

倒插進樹+取出重建+子樹處理+貪心遍歷

倒插進樹:把後綴轉化為前綴,所以把字符串倒著插進Trie中

取出重建:重新建立一棵以單詞為節點的樹,如果存在包含(前綴)關系就連邊

子樹處理:處理出每棵樹的大小用於貪心

貪心遍歷:遍歷整棵新樹,累加答案

關於貪心:每次找到最小的子樹統計答案

end.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using
namespace 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]背單詞