Japanese Student Championship 2021
E - Level K Palindrome
思路简单(?,代码调试半年。
做的路上出去口胡了2道莫队再回来做题,回来就想出了大框架,然后是各种细节调调调,然后调了3h+,中途换写法。
开始分析:(严肃脸
首先很 naive 的一个结论:
这个 level (k) 的串所对应的 level (0) 的串的长度是确认的。
啊?你说样例 (1) 不满足?那确实,因为长度为 (1) 的串都为 level (1) 。
这点需要特判处理。
我们可以处理出每一个位置对应相等的位置。(分治,然后回文对应相等,可以看代码理解)
这个信息用并查集维护。
然后按位贪心就可以了。
但是注意,按位贪心后得出的串是一个 level (0) 的串,所以这个串必须不回文。
那么在这个串已经是回文的情况下:
我们想要强制这个串不回文的同时保证答案最小,就是将一个位置出现次数最大的字符换为这个位置出现次数次大的字符。
这道题 差不多 就做完了
时间复杂度:(O(nlogn)) (n为字符串长度)
当然也只能是差不多做完了,还有好几个害人的细节:
毒瘤的细节:
1.判断无解时需要 (2^k) 这个东西,但是(kleq10^5) 防止爆 (int) 需要在无解中再加一个条件
2.如果最后得出的串是一个奇回文,那么不能替换中间的那个字符,只能替换两侧的字符
3.无解的条件中,最小的边界为 $2^{k-1} $ 所以应该是 (<) 而不是 (leq)。
Code:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)) f|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=f?-x:x;return;
}
template <typename T>
inline void print(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10^48);return;
}
const int N=5e5+3,K=5e5+3,INF=0x3f3f3f3f;
int k;
int fa[N],tong[N][30],maxn[N],emaxn[N],bel[N];
char s[N];
inline int Get(int x){return x==fa[x] ? x : fa[x]=Get(fa[x]);}
inline void Merge(int x,int y){fa[Get(x)]=Get(y);return;}
inline void Split(int l,int r,int dep){
if(!dep) return;
int len=(r-l+1);
if(len&1) Split(l,l+len/2-1,dep-1),Split(l+len/2+1,r,dep-1);
else Split(l,l+len/2-1,dep-1),Split(l+len/2,r,dep-1);
int mid=(l+r)>>1;
for(register int i=0;r-i>=mid;++i){Merge(r-i,l+i);}
return;
}
inline bool Judge(int len){
for(register int i=1;i<=len;++i) if(bel[i]!=bel[len-i+1]) return true;
return false;
}
vector<int> res;
int main(){
read(k);
scanf("%s",s+1);
int len=strlen(s+1);
if(k>=20||len/(1<<k)==1||len<(1<<(k-1))){puts("impossible");return 0;}
for(register int i=1;i<=len;++i) fa[i]=i;
int anslen=len,t=k;
while(t--){
if(anslen&1) anslen=(anslen-1)>>1;
else anslen>>=1;
}
Split(1,len,k);
for(register int i=1;i<=len;++i){
tong[Get(i)][s[i]-'a']++;
if(Get(i)==i) res.push_back(i);
}
int siz=res.size(),ans=0;
for(register int i=0;i<siz;++i){
int x=res[i];
for(register int j=0;j<26;++j){
if(maxn[x]<tong[x][j]) bel[x]=j,emaxn[x]=maxn[x],maxn[x]=tong[x][j];
else emaxn[x]=max(emaxn[x],tong[x][j]);
}
}
for(register int i=0;i<siz;++i) ans+=maxn[res[i]];
if(!anslen){print(len-ans);return 0;}
if(!Judge(anslen)){
int minn=INF;bool f=false;
if(anslen&1) f=true;
for(register int i=1;i<=anslen;++i){
if(f&&anslen/2+1==i) continue;
minn=min(minn,maxn[i]-emaxn[i]);
}
ans-=minn;
}
print(len-ans);
return 0;
}