P5841 题解
题目传送门
P5841
分析
先把所有字符串建到一颗 Trie 树上。
第一问
要求
引理:在一颗有
可以理解为:
和一个节点在一个方向上 dfs 虚越接近的节点,它们的 lca 的深度越大。
所以我们必然是按照字典树上的 dfs 序进行排列的。
我们在每一个字符串的末尾加一个特殊字符来避免前缀关系,便于处理。
所以,设虚根的深度为
第二问和第三问
由于第
我们对于 Trie 树上每一个节点的儿子们建若干个链表。
我们要求节点
于是我们写了一些函数:
inline bool check_first(int u,int x)
{
while(f[u] != x)
{
if(fir[f[u]]&&fir[f[u]] != u) return 0;
if(pre[u]) return 0;
if(tail[u]&&tail[u] == las[f[u]]&&len[u] != child[f[u]]) return 0;
u = f[u];
}
return 1;
}
inline bool check_last(int u,int x)
{
while(f[u] != x)
{
if(las[f[u]]&&las[f[u]] != u) return 0;
if(nxt[u]) return 0;
if(head[u]&&head[u] == fir[f[u]]&&len[head[u]] != child[f[u]]) return 0;
u = f[u];
}
return 1;
}
inline bool check(int u,int v,int x)
{
while(f[u] != x) u = f[u];
while(f[v] != x) v = f[v];
if(nxt[u] == v&&pre[v] == u) return 1;
if((nxt[u]&&nxt[u] != v)||(pre[v]&&pre[v] != u)) return 0;
if(head[u] == v&&tail[v] == u) return 0;
if(fir[x]&&las[x]&&head[u] == fir[x]&&tail[v] == las[x]&&len[head[u]] + len[v] != child[x]) return 0;
if(u == las[x]||v == fir[x]) return 0;
return 1;
}
我们要判断的有三点:
-
满足链表的关系
-
能否接在特定位置的前面/后面
-
如果开头到结尾的链表接起来,其长度必须等于其子树个数
如果满足,就把
if(check_last(u,x)&&check_first(v,x)&&check(u,v,x))
{
while(f[u] != x)
{
las[f[u]] = u;
u = f[u];
}
while(f[v] != x)
{
fir[f[v]] = v;
v = f[v];
}
if(pre[v] != u||nxt[u] != v)
{
pre[v] = u;
nxt[u] = v;
head[tail[v]] = head[u];
tail[head[u]] = tail[v];
len[head[u]] += len[v];
len[v] = 0;
head[u] = tail[v] = 0;
}
++ans;
mark[i] = 1;
}
这个链表的判断和操作较为复杂,写之前建议自己在草稿纸分析一下情况。
最后,再按照链表确定的 dfs 序输出答案。
此时的时间复杂度是:
优化
可以发现,在考虑 dfs 序时,一些很长的链是无用的,考虑压缩。
对于仅有一个儿子的节点,可以省略它。
int build(int u)
{
if(!child[u]) return u;
F(i,0,26)
if(son[u][i])
{
int v = build(son[u][i]);
if(child[u] == 1) return v;
g[u].push_back(v);
f[v] = u;
}
return u;
}
rt = build(1);
此时,Trie 树的最大深度约为
故时间复杂度为
就可以通过此题了!
代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(register T &x)
{
register T p = 1;
x = 0;
char c = getchar();
while(c < '0'||c > '9')
{
if(c == '-') p = -p;
c = getchar();
}
while('0' <= c&&c <= '9')
{
x = (x<<3)+(x<<1)+(c^48);
c = getchar();
}
x *= p;
}
template<typename T>inline void write(register T x)
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) write(x/10);
putchar(x%10+48);
}
#define D(i,a,b) for(register int i=a;i>=b;--i)
#define F(i,a,b) for(register int i=a;i<=b;++i)
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define N 200010
bitset<N> mark;
vector<int> g[N];
ll sum = 0;
pii p[N];
int son[N][30],f[N],child[N],ed[N],id[N],dep[N];
int head[N],tail[N],nxt[N],pre[N],len[N],fir[N],las[N];
int n,m,cnt = 1,rt = 0,ans = 0;
string s;
int build(int u)
{
if(!child[u]) return u;
F(i,0,26)
if(son[u][i])
{
int v = build(son[u][i]);
if(child[u] == 1) return v;
g[u].push_back(v);
f[v] = u;
}
return u;
}
inline int lca(int u,int v)
{
if(dep[u] < dep[v]) swap(u,v);
while(dep[u] > dep[v]) u = f[u];
while(u != v) u = f[u],v = f[v];
return u;
}
void dfs(int u)
{
if(!child[u])
{
write(ed[u]);
putchar(' ');
return;
}
vector<int> e[3];
for(auto v:g[u])
{
if(pre[v]) continue;
int lb = 1;
if(fir[u]&&v == fir[u]) --lb;
if(las[u]&&tail[v] == las[u]) ++lb;
int t = v;
while(t)
{
e[lb].push_back(t);
t = nxt[t];
}
}
F(i,0,2)
for(auto j:e[i])
dfs(j);
}
inline bool check_first(int u,int x)
{
while(f[u] != x)
{
if(fir[f[u]]&&fir[f[u]] != u) return 0;
if(pre[u]) return 0;
if(tail[u]&&tail[u] == las[f[u]]&&len[u] != child[f[u]]) return 0;
u = f[u];
}
return 1;
}
inline bool check_last(int u,int x)
{
while(f[u] != x)
{
if(las[f[u]]&&las[f[u]] != u) return 0;
if(nxt[u]) return 0;
if(head[u]&&head[u] == fir[f[u]]&&len[head[u]] != child[f[u]]) return 0;
u = f[u];
}
return 1;
}
inline bool check(int u,int v,int x)
{
while(f[u] != x) u = f[u];
while(f[v] != x) v = f[v];
if(nxt[u] == v&&pre[v] == u) return 1;
if((nxt[u]&&nxt[u] != v)||(pre[v]&&pre[v] != u)) return 0;
if(head[u] == v&&tail[v] == u) return 0;
if(fir[x]&&las[x]&&head[u] == fir[x]&&tail[v] == las[x]&&len[head[u]] + len[v] != child[x]) return 0;
if(u == las[x]||v == fir[x]) return 0;
return 1;
}
int main()
{
// freopen("reorder.in","r",stdin);
// freopen("reorder.out","w",stdout);
read(n),read(m);
F(i,1,n)
{
cin >> s;
s.push_back('a'-1);
int u = 1;
F(j,0,s.size()-1)
{
int ch = s[j]-'a'+1;
if(!son[u][ch])
{
son[u][ch] = ++cnt;
if(++child[u] >= 2) sum += 1ll * j * j;
}
u = son[u][ch];
}
ed[u] = i;
id[i] = u;
}
rt = build(1);
dep[rt] = 1;
F(i,1,cnt)
{
if(f[i]) dep[i] = dep[f[i]] + 1;
head[i] = tail[i] = i;
len[i] = 1;
}
F(i,1,m) read(p[i].first),read(p[i].second);
D(i,m,1)
{
int u = id[p[i].first],v = id[p[i].second],x = lca(u,v);
if(check_last(u,x)&&check_first(v,x)&&check(u,v,x))
{
while(f[u] != x)
{
las[f[u]] = u;
u = f[u];
}
while(f[v] != x)
{
fir[f[v]] = v;
v = f[v];
}
if(pre[v] != u||nxt[u] != v)
{
pre[v] = u;
nxt[u] = v;
head[tail[v]] = head[u];
tail[head[u]] = tail[v];
len[head[u]] += len[v];
len[v] = 0;
head[u] = tail[v] = 0;
}
++ans;
mark[i] = 1;
}
}
write(sum),putchar('\n');
write(ans),putchar(' ');
F(i,1,m)
if(mark[i])
write(i),putchar(' ');
putchar('\n');
dfs(rt);
return 0;
}