题解 CF1312G 【Autocompletion】

xtx1092515503

2020-07-23 08:46:16

Solution

很明显按照题意建出来的东西是一颗trie。 于是我们就设$f[i]$表示到达字典树上第$i$个点最少步数。 则我们如果设$delta_i$表示$i$节点是树上字典序第$delta_i$小的节点的话,我们就可以得到转移方程: $$f[i]=\max\begin{cases}f[fa]+1\\f[anc]-delta_{anc}+delta_i\end{cases}$$ 上半部可以直接$O(1)$转移,关键是下半部。 我们可以直接将$f[anc]-delta_{anc}$压到一个栈中,如果发现新到达的这个点的$f[x]-delta_x$要小于栈顶的元素,就直接入栈。然后转移时就直接使用栈顶元素即可。当离开一个节点时,弹出它对应的元素(假如那个数还在栈中的话)。 然后我们发现这个$delta$数组实际上都不用建出来,直接用一个值$delta$维护即可。 复杂度$O(n)$。 代码(29行小清新马蜂,你值得拥有): ```cpp #include<bits/stdc++.h> using namespace std; int n,m,delta,q[1001000]; struct trie{ int ch[26],f; bool fin; }t[1001000]; stack<pair<int,int> >s; void dfs(int x){ if(s.empty()||t[x].f-delta<s.top().second)s.push(make_pair(x,t[x].f-delta)); delta+=t[x].fin; for(int i=0;i<26;i++){ if(!t[x].ch[i])continue; t[t[x].ch[i]].f=t[x].f+1; if(!s.empty()&&t[t[x].ch[i]].fin)t[t[x].ch[i]].f=min(t[t[x].ch[i]].f,s.top().second+delta+1); dfs(t[x].ch[i]); } if(!s.empty()&&s.top().first==x)s.pop(); } char str[2]; int main(){ scanf("%d",&n); for(int i=1,x;i<=n;i++)scanf("%d%s",&x,str),t[x].ch[str[0]-'a']=i; scanf("%d",&m); for(int i=1,x;i<=m;i++)scanf("%d",&q[i]),t[q[i]].fin=true; dfs(0); for(int i=1;i<=m;i++)printf("%d ",t[q[i]].f); return 0; } ```