题解 AtCoder-s8pc_5_h【Percepts of Atcoder】/ SS221112D【W】 / ZROI1410【超简单题】
problem
将字符串 \(S\) 的所有非空的可以不连续的子序列拎出来去重并排序,\(q\leq 10^5\) 次询问第 \(k\) 小字典序的子序列的后 \(p\) 个字符。\(|S|,\sum p\leq 10^5,k\leq 10^{18}\)。
solution 0
建出子序列自动机,然后乱搞!\(O(nq)\)。
我们复习一下子序列自动机:
- 首先这是个自动机。
- 子序列自动机是一个 DAG,每一条路径唯一的对应了原串的一个子序列。
- 我们可以令 \(f_i\) 表示从点 \(i\) 出发能到达的路径的个数,是 DAG 上 DP。
- 时间 \(O(n)\),空间 \(O(n|\Sigma|)\)。
void build(){
fill(last,last+26,n+1);
memcpy(to[n+1],last,sizeof last);
f[n+1]=0;
for(int i=n;i>=0;i--){
memcpy(to[i],last,sizeof last);
f[i]=1;
for(int j=0;j<26;j++) red(f[i]+=f[to[i][j]]);
if(1<=i&&i<=n) last[a[i]-'a']=i;
}
}
有个细节:\(f\) 可能很大,我们对 \(f\) 和 \(10^{18}\) 取个 \(\min\) 就好了。
solution 1
DAG 链剖分你可曾听过?
像重链剖分一样,我们选出一个 \(son_u\) 表示 \(\max_{u\to v}f_v\) 的 \(v\),并说 \(u\to son_u\) 是重边,其它是轻边。
和树剖一样,每条一次轻边,要么是 \(\infty\to 10^{18}\),要么是 \(k\to k/2\),方案数至少除以 \(2\),总的跳轻边的复杂度 \(O(\log k|\Sigma|)\)。
但是我们要跳重链,和树剖不同的是,我们走的重边只是重链的一段,所以我们倍增一下找到最远能跳到那里,轻松的。跳一次重链 \(O(\log |S|)\),一共 \(O(\log k)\) 条。
这样我们将一个子序列表示成 \(O(\log |S|)\) 个重链和轻边的交错,输出后 \(p\) 个字符就倒着扫就行。
总的复杂度是 \(O\left(|S|\log k\left(\log |S|+|\Sigma|\right)\right)\)。
code
细节:怎么知道能跳到哪里?倍增的时候,算一下跳完之后能减掉多少方案数,同时要求跳过去之后还在重链上。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
constexpr LL inf = 2e18;
int n, lst[26], to[300010][26], son[300010], q, p;
LL f[300010];
char a[300010], stk[1000010];
pair<LL, int> st[20][300010];
void putc(int ch) {
if (p) stk[p--] = ch + 'a';
}
void print(int j, int u) {
if (j) {
if (p) print(j - 1, st[j - 1][u].second);
if (p) print(j - 1, u);
} else {
putc(son[u]);
}
}
void dfs(int i, LL k) {
if (k-- == 0) return ;
vector<pair<int, int>> sta;
for (int j = 19; j >= 0; j--) if (st[j][i].second <= n && k >= st[j][i].first && k - st[j][i].first + 1 < f[st[j][i].second]) {
sta.emplace_back(j, i);
k -= st[j][i].first;
i = st[j][i].second;
}
for (int j = 0; j < 26; j++) {
if (k >= f[to[i][j]]) k -= f[to[i][j]];
else { dfs(to[i][j], k), putc(j); break; }
}
reverse(sta.begin(), sta.end());
for (auto e : sta) print(e.first, e.second);
}
int main() {
#ifndef LOCAL
cin.tie(nullptr)->sync_with_stdio(false);
#endif
cin >> a;
n = strlen(a);
fill(lst, lst + 26, n + 1);
memcpy(to[n + 1], lst, sizeof lst);
st[0][n + 1] = make_pair(0, n + 1);
for (int i = n; i >= 0; i--) {
memcpy(to[i], lst, sizeof lst);
f[i] = 1;
for (int j = 0; j < 26; j++) {
f[i] = min(f[i] + f[to[i][j]], inf);
if (f[to[i][j]] > f[to[i][son[i]]]) son[i] = j;
}
LL pre = 1;
for (int j = 0; j < son[i]; j++) pre = min(pre + f[to[i][j]], inf);
st[0][i] = make_pair(pre, to[i][son[i]]);
if (i) lst[a[i - 1] - 'a'] = i;
}
for (int j = 1; j <= 19; j++) {
for (int i = 0; i <= n + 1; i++) {
int nxt = st[j - 1][i].second;
st[j][i] = make_pair(min(st[j - 1][i].first + st[j - 1][nxt].first, inf), st[j - 1][nxt].second);
}
}
cin >> q;
while (q--) {
LL k;
cin >> k >> p;
stk[p + 1] = 0;
if (k >= f[0]) cout << -1 << endl;
else {
dfs(0, k);
cout << (stk + p + 1) << endl;
}
}
return 0;
}
点击查看代码
typedef long long LL;
LL mod(LL x){return min(x,(LL)2e18);}
void red(LL&x){x=mod(x);}
struct node{
LL sum; int last;
node(LL sum=0,int last=0):sum(sum),last(last){}
node operator+(node b){return node(mod(sum+b.sum),b.last);}
};
int n,to[300010][26],last[26],son[300010];
LL f[300010];
char a[300010];
node st[20][300010];
void build(){
fill(last,last+26,n+1);
memcpy(to[n+1],last,sizeof last);
son[n+1]=0,st[0][n+1]=node(0,n+1),f[n+1]=0;
for(int i=n;i>=0;i--){
memcpy(to[i],last,sizeof last);
for(int j=0;j<26;j++) if(f[to[i][j]]>f[to[i][son[i]]]) son[i]=j;
f[i]=1;
for(int j=0;j<26;j++) red(f[i]+=f[to[i][j]]);
LL sum=1;
for(int j=0;j<son[i];j++) red(sum+=f[to[i][j]]);
st[0][i]=node(sum,to[i][son[i]]);
if(1<=i&&i<=n) last[a[i]-'a']=i;
}
for(int j=1;j<=19;j++){
for(int i=0;i<=n+1;i++) st[j][i]=st[j-1][i]+st[j-1][st[j-1][i].last];
}
}
int top;
char stk[1000010];
void print(int j,int i){
if(j==0) return top&&(stk[--top]=son[i]+'a'),void();
if(top) print(j-1,st[j-1][i].last);
if(top) print(j-1,i);
}
void solve(int u,LL k){
if(k==1) return ;
vector<pair<int,int>> used={};
for(int j=19;j>=0;j--){
if(k>st[j][u].sum&&k-st[j][u].sum<=f[st[j][u].last]){
used.push_back({j,u});
debug("k=%lld, jump st[%d][%d]={%lld,%d}\n",k,j,u,st[j][u].sum,st[j][u].last);
k-=st[j][u].sum;
u=st[j][u].last;
}
}
if(k>1){
k--;
for(int j=0;j<26;j++){
if(k>f[to[u][j]]) k-=f[to[u][j]];
else{solve(to[u][j],k),top&&(stk[--top]=j+'a');break;};
}
}
for(int j=(int)used.size()-1;j>=0;j--) print(used[j].first,used[j].second);
}
int main(){
scanf("%s%*d",a+1),n=strlen(a+1);
build();
for(LL k,p;~scanf("%lld%lld",&k,&p);){
if(++k>f[0]) puts("-1");
else solve(stk[top=p]=0,k),puts(stk+top);
}
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-SS221112D.html