AT_s8pc_5_h Percepts of Atcoder题解
参考 这篇题解 的时候感觉时间复杂度可以卡掉,所以我补充一下 DAG 链剖分这个做法怎么做。
暴力需要找到字典序排在第 \(k\) 的子序列,考虑建立序列自动机,也就是从起始点开始向下走,到任意点的每条路径都对应一个原字符串的子序列。
为了便于求出后面的 \(p\) 个字符,我们可以想到先用某些方法压缩一下路径,再把需要使用的路径部分展开。
这时候可以回想一下在树上问题时我们采用重链剖分压缩路径,那么在这个问题中,我们也可以采用类似的方法。
得出序列自动机后,我们可以轻易得到这个点为起点有多少走法。
然后我们把一个点到其儿子中走法最多的点之间的边连接为重边。
由于每个点的走法同样是通过求和得到的,与树的子树大小类似,再加上因为 \(k\) 的范围限制最大不需要超过 \(10^{18}\) 的条件。我们将每个节点的走法和 \(10^{18}\) 取较小值,若儿子中存在大小为 \(10^{18}\) 的,那么重儿子就设置为最前面的 \(10^{18}\),因为这个值最大,后面的点也就没有机会了,不影响,所以可以得到走的轻边数量与树链剖分相似,是 \(\log siz\),而其中的 \(siz\le 10^{18}\)。
考虑重链如何压缩,我们考虑倍增,预处理出每一步到达的位置是否包含要走的子序列,可以直接预处理,需要注意边界条件。
剩下的问题在于解除压缩,参考的题解中会把用到的重链直接全部展开,这其实是有问题的,如果有多次询问都展开了一个长度接近原串的重链,时间复杂度就会退回 \(O(Q|S|)\) 了。
因此,在处理到最后一个重链时,我们先倍增到达需要加入的点,然后再展开后面德内容进行处理,这样只会给每次询问加上一个 \(O(\log |S|)\) 的时间复杂度,总的时间复杂度不变。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18;
char s[300005];
int n,Q,son[300005][26],hson[300005],cnt[300005],fa[300005][20],st[300005][20],L[300005][20],R[300005][20];
string query(int k,int p){
if(k>cnt[0])return "-1";
vector<pair<int,pair<int,int>>> ve;
int w=0;
while(k){
for(int i=19;i>=0;i--){
if(k>=L[w][i]&&k<=R[w][i]){
ve.push_back({1,{w,i}});
k-=st[w][i];
w=fa[w][i];
}
}
if(w)k--;
if(!k)break;
for(int i=0;i<26;i++){
if(cnt[son[w][i]]>=k){
ve.push_back({0,{w,i}});
w=son[w][i];
break;
}
k-=cnt[son[w][i]];
}
}
reverse(ve.begin(),ve.end());
string ans="";bool fl=0;
for(auto tmp:ve){
if(tmp.first){
int x=tmp.second.first;
string t="";
if((1<<tmp.second.second)+ans.size()<=p){
for(int j=1;j<=(1<<tmp.second.second)&&x!=n+1;j++)t+=(char)(hson[x]+'a'),x=fa[x][0];
}
else {
int d=(1<<tmp.second.second)-(p-ans.size());
for(int j=19;j>=0;j--)if(d>>j&1)x=fa[x][j];
for(int j=1;j<=p-ans.size()&&x!=n+1;j++)t+=(char)(hson[x]+'a'),x=fa[x][0];
}
reverse(t.begin(),t.end());
ans+=t;
if(x==n+1)break;
}
else {
if(tmp.second.first==n+1)break;
ans+=(char)(tmp.second.second+'a');
}
if(ans.size()==p)break;
}
reverse(ans.begin(),ans.end());
return ans;
}
signed main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=0;i<26;i++)son[n][i]=n+1;
cnt[n]=1;
for(int i=n-1;i>=0;i--){
for(int j=0;j<26;j++)son[i][j]=son[i+1][j];
son[i][s[i+1]-'a']=i+1;
if(i)cnt[i]=1;
int ma=-inf;
for(int j=0;j<26;j++){
if(!son[i][j])continue;
if(ma<cnt[son[i][j]]){
hson[i]=j;
fa[i][0]=son[i][j];
st[i][0]=cnt[i];
L[i][0]=cnt[i]+1;
R[i][0]=min(cnt[i]+cnt[son[i][j]],inf);
ma=cnt[son[i][j]];
}
cnt[i]=min(cnt[i]+cnt[son[i][j]],inf);
}
}
fa[n+1][0]=n+1,L[n+1][0]=1,R[n+1][0]=0;
for(int i=1;i<20;i++){
for(int j=0;j<=n+1;j++){
int u=fa[j][i-1];
fa[j][i]=fa[u][i-1];
st[j][i]=min(st[j][i-1]+st[u][i-1],inf);
L[j][i]=max(st[j][i-1]+L[u][i-1],L[j][i-1]);
R[j][i]=min(st[j][i-1]+R[u][i-1],R[j][i-1]);
}
}
cin>>Q;
while(Q--){
int k,p;cin>>k>>p;
cout<<query(k,p)<<endl;
}
return 0;
}

浙公网安备 33010602011771号