多校 5
D
设\(i\)、\(j\)均为可行位置,我们需要证明,只有求出\([1,i]\)字典序最小的串有用
这里用\([1,i]\)表示\([1,i]\)的一种方案,\([i,1]\)表示它的翻转
有\(4\)种\(j\)处的可能情况
\(j\)不翻转
\([1, i]\) + \([i+1,j ]\)
\([i, 1]\) + \([j+1, i]\)
(这种情况可以表示只先进行(或者撤销)\([i, 1]\)翻转
\([j, i +1]\) +\([i, 1]\)只进行\(j\)翻转
\([j, i+1]\) +\([1, i]\)
若\([1, i]\)是字典序最小的串,那么几种情况中只有第一种和第四种可能贡献答案,我们将\([1, i]\)的信息维护出来,再判断哪一种字典序更小即可(用hash判断lcp再比较)
具体地,\([1,i]\)的构造过程中有两种字符:在头部插入(对应4)或者在尾部插入(对应1)
我们将其从中间分开,相当于两个队列,插入字符时,我们分别\(O(1)\)维护队列内部的hash,查询前缀hash时候,我们将队列两侧拼起来即可
注意细节:
ull 哈希不能取模,可能在减法时出现负数,爆掉ull
给定一个数字串,你可以翻转前缀,有几个前缀不能翻转,只能从小前缀到大前缀翻转,求最小字典序
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef ll hst;
const int N = 3e5+10;
ll qpow(ll a, ll b, ll MOD){
ll ret = 1;
for(;b;b>>=1){
if(b & 1) ret = ret * a % MOD;
a = a * a% MOD;
}
return ret;
}
ll inv(ll x, ll MOD){
return qpow(x, MOD-2, MOD);
}
struct t_ans{
static const hst base = 37, MOD = 998244353;
hst pw[N];
hst operator ()(int ord[], int len){
hst res = 0;
pw[0] = 1;
for(int i = 1; i <= len; ++i) pw[i] = pw[i-1] * base %MOD;
for(int i = 1; i <= len; ++i){
res = (res + pw[i-1] * ord[i] % MOD)%MOD;
}
return res;
}
}hscode;
const hst B = 131, MOD = 998244853, iB = inv(B, MOD);
hst base[N], ibase[N];
void init_base(int len){
ibase[0] = base[0] = 1;
for(int i = 1; i <= len; ++i) base[i] = (base[i-1] * B)%MOD;
for(int i = 1; i <= len; ++i) ibase[i] = (ibase[i-1] * iB)%MOD;
}
struct t_seq{
hst lef[N], rig[N];
hst strlef[N], strrig[N];
int cntl, cntr;
int size(){
return cntr + cntl;
}
void clear(){
cntl = cntr = 0;
rig[0] = lef[0] = 0;
}
void push_rig(int x){
++cntr, rig[cntr] = (x + rig[cntr-1] * B)%MOD;
strrig[cntr] = x;
}
void push_lef(int x){
++cntl, lef[cntl] = (x * base[cntl-1] + lef[cntl-1])%MOD;
strlef[cntl] = x;
}
void pop_rig(){
--cntr;
}
void pop_lef(){
--cntl;
}
hst sub_lef(int l, int r){
if(l > r) return 0;
return (lef[r] + MOD- lef[l-1])%MOD * ibase[l-1]%MOD;
}
hst sub_rig(int l, int r){
if(l > r) return 0;
return (rig[r] - rig[l-1] * base[r-l+1]%MOD)%MOD;
}
hst prehash(int r){
if(r > cntl){
return (sub_lef(1, cntl) * base[r-cntl]%MOD + sub_rig(1 ,r-cntl))%MOD;
}else{
return sub_lef(cntl-r+1, cntl)%MOD;
}
}
int operator [](int x){
if(x <= cntl) return strlef[cntl - x + 1];
return strrig[x - cntl];
}
void get_seq(int ord[]){
for(int i = cntl; i >= 1; --i) ord[++ord[0]] = strlef[i];
for(int i = 1; i <= cntr; ++i) ord[++ord[0]] = strrig[i];
}
void prt(){
for(int i = 1; i <= size(); ++i){
cerr<<((*this)[i])<< " ";
}
cerr<<endl;
}
}seq[2];//通常情况下一样
bool check(int mid){
return seq[0].prehash(mid) == seq[1].prehash(mid);
}
int get_lcp(){
int res = 0, l = 0, r = seq[0].size();
while(l <= r){
int mid = (l + r) >> 1;
if(check(mid)){
res = mid;
l = mid + 1;
}else{
r = mid-1;
}
}
return res;
}
int cmp(){
int res = get_lcp();
if(res == seq[0].size()) return 0;
return seq[0][res + 1] < seq[1][res + 1] ? -1 : 1;
}
int n,m;
int a[N];
int res[N];
bool ban[N];
int pre[N];
void solve(){
seq[0].clear(), seq[1].clear();
cin >> n >> m;
init_base(n);
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int i = 1; i <= n; ++i) ban[i] = false;
for(int i = 1; i <= m; ++i){
int x;cin>>x; ban[x]= true;
}
pre[0] = 0;
for(int i = 1; i <= n; ++i){
if(ban[i-1]) pre[i] = pre[i - 1];
else pre[i] = i;
}
for(int i = 1; i <= n; ++i){
if(ban[i]) continue;
for(int j = pre[i]; j <= i; ++j){
seq[0].push_rig(a[j]);
}
for(int j = pre[i]; j <= i; ++j){
seq[1].push_lef(a[j]);
}
if(cmp() <= 0){//seq[0]更优秀
for(int j = pre[i]; j <= i; ++j) seq[1].pop_lef();
for(int j = pre[i]; j <= i; ++j) seq[1].push_rig(a[j]);
}else{
for(int j = pre[i]; j <= i; ++j) seq[0].pop_rig();
for(int j = pre[i]; j <= i; ++j) seq[0].push_lef(a[j]);
}
if(i == 5){
seq[0].prt();
}
}
if(ban[n]){
for(int i = pre[n]; i <= n;++i){//插入最后一个非法段
seq[0].push_rig(a[i]);
}
}
for(int i = 1; i <= n; ++i){
res[i] = seq[0][i];
}
cout << hscode(res, n) << endl;
}
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}