P5841 题解
DaiRuiChen007 · · 题解
P5841 题解
Problem Link
题目大意
给定
n 个由小写字母组成的两两不同的非空字符串S_1,S_2,\cdots , S_n ,对于一个1 到n 的排列P=(p_1,p_2,\cdots,p_n) ,定义P 的价值W(P)= \sum_{i=2}^n (\text{lcp}(S_{p_i-1},S_{p_i}))^2 ,设能够产生最大价值的排列为P^*_G 。此外,还有
q 个附加任务。对于第i 个任务,给定两个1 到n 之间的不同的整数X_i 和Y_i 。对于排列P ,若P 在满足W(P) = W(P^*_G) 的前提条件之下,同时满足第X_i 个字符串S_{X_i} 恰好排在第Y_i 个字符串S_{Y_i} 之前, 即\text{pos}(S_{X_i}) + 1 = \text{pos}(S_{Y_i}) ,其中\text{pos}(S_i) 表示字符串S_i 在排列中的位置,则排列P 还将获得2^i 的奖励。所有任务的奖励之和称之为总任务奖励。我们设能够使得总任务奖励最大的排列为
P^*_B 。试求:
数据范围:
Q\le 10^5,L=\sum |S_i|\le 2\times 10^5 。
思路分析
为了防止产生前后缀关系,我们给每个
然后看附加任务,显然倒序满足最优,对于每个任务,可以看做钦定某两个点在 dfs 序中相邻。
观察发现每条限制对每个节点
显然用链表维护每个点儿子的搜索次序即可,更新时简单分讨一下,由于每个节点儿子数是
此时瓶颈在处理路径上的每一个点,考虑优化:注意到 Trie 树上有很多出度为
观察简化 Trie 树的最大深度,容易发现使最大深度从
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+5;
string str[MAXN];
int tot=1,edp[MAXN],tr[MAXN][27],deg[MAXN],bel[MAXN],pi[MAXN];
vector <int> G[MAXN];
int dep[MAXN],kfa[MAXN][20],fa[MAXN];
inline void init(int u,int Fa) {
fa[u]=kfa[u][0]=Fa,dep[u]=dep[Fa]+1;
for(int k=1;k<20;++k) kfa[u][k]=kfa[kfa[u][k-1]][k-1];
for(int v:G[u]) init(v,u);
}
int st[MAXN],ed[MAXN]; //first and last son
int suf[MAXN],pre[MAXN],hd[MAXN],tl[MAXN],siz[MAXN]; //list<>
int lu[MAXN],lv[MAXN];
vector <int> ord;
inline void dfs(int u) {
if(bel[u]) ord.push_back(bel[u]);
vector <int> sons;
for(int v:G[u]) if(hd[v]==v) sons.push_back(v);
auto q=[&](int v) {
if(hd[v]==st[u]) return -1;
if(tl[v]==ed[u]) return 1;
return 0;
};
sort(sons.begin(),sons.end(),[&](int x,int y) {
return q(x)==q(y)?x<y:q(x)<q(y);
});
for(int v:sons) for(int i=hd[v];i;i=suf[i]) dfs(i);
}
signed main() {
freopen("reorder.in","r",stdin);
freopen("reorder.out","w",stdout);
ios::sync_with_stdio(false);
int n,m;
cin>>n>>m;
ll sum=0;
for(int i=1;i<=n;++i) {
cin>>str[i],str[i].push_back('z'+1);
int p=1;
for(int j=0;j<(int)str[i].length();++j) {
int c=str[i][j]-'a';
if(!tr[p][c]) {
++deg[p],tr[p][c]=++tot;
if(deg[p]>=2) sum+=j*j;
}
p=tr[p][c];
}
edp[i]=p,bel[p]=i;
}
cout<<sum<<"\n";
for(int i=tot;i>1;--i) {
if(deg[i]==1) {
for(int c=0;c<=26;++c) if(tr[i][c]) pi[i]=pi[tr[i][c]];
} else {
pi[i]=i;
for(int c=0;c<=26;++c) if(tr[i][c]) G[i].push_back(pi[tr[i][c]]);
}
}
for(int c=0;c<=26;++c) if(tr[1][c]) G[1].push_back(pi[tr[1][c]]);
init(1,0);
vector <int> lim;
for(int i=1;i<=m;++i) cin>>lu[i]>>lv[i];
for(int i=1;i<=tot;++i) hd[i]=tl[i]=i,siz[i]=1,pre[i]=suf[i]=st[i]=ed[i]=0;
for(int i=m;i>=1;--i) {
int u=edp[lu[i]],v=edp[lv[i]],x=u,y=v;
if(dep[x]>dep[y]) {
for(int k=19;~k;--k) if(dep[kfa[x][k]]>=dep[y]) x=kfa[x][k];
} else {
for(int k=19;~k;--k) if(dep[kfa[y][k]]>=dep[x]) y=kfa[y][k];
}
for(int k=19;~k;--k) if(kfa[x][k]^kfa[y][k]) x=kfa[x][k],y=kfa[y][k];
int z=fa[x];
bool ok=true;
for(int p=u;fa[p]!=z;p=fa[p]) {
if(ed[fa[p]]&&ed[fa[p]]!=p) ok=false;
if(suf[p]) ok=false;
if(st[fa[p]]==hd[p]&&siz[p]<deg[fa[p]]) ok=false;
}
for(int p=v;fa[p]!=z;p=fa[p]) {
if(st[fa[p]]&&st[fa[p]]!=p) ok=false;
if(pre[p]) ok=false;
if(ed[fa[p]]==tl[p]&&siz[p]<deg[fa[p]]) ok=false;
}
if(suf[x]!=y) {
if(x==ed[z]||y==st[z]) ok=false;
if(hd[x]==hd[y]) ok=false;
if(suf[x]||pre[y]) ok=false;;
if(hd[x]==st[z]&&tl[y]==ed[z]&&siz[x]+siz[y]<deg[z]) ok=false;
}
if(ok) {
lim.push_back(i);
for(int p=u;fa[p]!=z;p=fa[p]) ed[fa[p]]=p;
for(int p=v;fa[p]!=z;p=fa[p]) st[fa[p]]=p;
if(suf[x]!=y) {
int nowh=hd[x],nowt=tl[y],nows=siz[x]+siz[y];
suf[x]=y,pre[y]=x;
for(int i=nowh;i;i=suf[i]) hd[i]=nowh,tl[i]=nowt,siz[i]=nows;
}
}
}
reverse(lim.begin(),lim.end());
cout<<lim.size()<<" ";
for(int i:lim) cout<<i<<" "; cout<<"\n";
dfs(1);
for(int i:ord) cout<<i<<" "; cout<<"\n";
return 0;
}