「題解」路過中丹 pass
本文將同步釋出於:
題目
題目描述
丹不僅是 OI 之神、whk 之神,還是遊戲之神。
現在有一個字串,你可以選擇從字串上面的某個位置 \(p\) 開始在字串上面行走,每次你只能向你當前所在位置的左邊或者右邊走一步(也就是說,如果你當前所在位置為 \(t\),那麼你下一步可以走到 \(t-1\) 或者 \(t+1\)),當然你不能走出這個字串,最後你會選擇在某個位置 \(q\) 結束你的行走,你需要保證 \(p\neq q\) 並且將你依次
經過的字元(包括起點和終點)寫下來得到是一個迴文串。
稱一個字串是“配得上丹”的,當且僅當你可以通過若干次行走使得這個字串的每個位置都被經過至少一次。
現在給定一個長度為 \(n\) 的字串 \(s\),有 \(q\) 次詢問,第 \(i\) 次詢問給出兩個數 \(l_i,r_i\),你需要判斷 \(s\) 中 \(l_i\) 到 \(r_i\) 的這個子串是否是“配得上丹”的。
\(n,q\leq 10^6\)。
題解
冷靜地判定
奇數判定
首先觀察樣例二的第二次詢問,cdbaca
是合法的串,我們不難猜想,一個含有長度 \(\geq 3\) 的奇迴文串必定合法。
我們冷靜證明一下這個命題。
考慮奇迴文串 \(a\),貪心地考慮可以把它拆到只剩下 \(3\) 個字元,因為多餘的字元都沒有用。
考慮字串 \(xXaYy\),我們不難設 \(a=a_1a_2a_3\)
偶數判定
我們再考慮部分分特殊性質 A,猜想一個字串除上述方案合法外,合法當且僅當其可被多個偶迴文串覆蓋。
考慮證明,我們不妨先轉化一下題意,即兩個不同的出發點上各有兩個人,兩個人每次走相同字母,最後相遇,形成一個迴文串。
我們證明一個引理,兩個人不會有其中一個人單獨走回頭路,畫圖分析兩個人。
考慮上圖,其中紅圈為起點,對應的線段之間字元相同,分析可知,兩個灰色叉處存在奇迴文串,矛盾,因此引理得證。
因此我們得出結論,兩個人的方向永遠相向(相離)或平行,若平行,則必有一人先回頭,不可能,若相向(相離則同時回頭,與相向沒有區別)則形成偶迴文串,故合法路徑均為偶迴文串,命題得證。
簡單又自然
我們不難發現,奇迴文串我們只需要標記每個中心點,然後字首和判定即可。
對於偶迴文串,我們考慮有哪些點不可被區間內的偶迴文串覆蓋。
對於每個點,我們考慮覆蓋了其的迴文串,不難發現,有用的串只有兩個:以它為左右端點的最短串。
考慮左串 \([i-\texttt{lef}_i+1,i]\),右串 \([i,i+\texttt{rig}_i-1]\),不難發現,若左右端點滿足 \(l\in(i-\texttt{lef}_i+1,i]\),\(r\in[i,i+\texttt{rig}_i-1)\),那麼必定無法覆蓋 \(i\),子串不合法。
這對應著 \(xOy\) 平面上的一個矩形,我們只需要求出矩形並即可判定,若能求出矩形,我們離線詢問後掃描線即可。
馬拉車演算法
我們很容易發現,一個最短的迴文串必然對應一個最近的迴文中心,我們考慮用 manacher 演算法求解迴文中心對應半徑,用單調棧維護所有中心即可,時間複雜度為 \(\Theta(n)\),十分優秀。
參考程式
用樹狀陣列實現掃描線的判定問題,常數更小。
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
bool st;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
static char buf[1<<21],*p1=buf,*p2=buf;
#define flush() (fwrite(wbuf,1,wp1,stdout),wp1=0)
#define putchar(c) (wp1==wp2&&(flush(),0),wbuf[wp1++]=c)
static char wbuf[1<<21];int wp1;const int wp2=1<<21;
inline int read(void){
reg char ch=getchar();
reg int res=0;
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) res=10*res+(ch^'0'),ch=getchar();
return res;
}
inline char* read(reg char* s){
*s=getchar();
while(!isalpha(*s)) *s=getchar();
while(isalpha(*s)) *(++s)=getchar();
*s='\0';
return s;
}
inline int max(reg int a,reg int b){
return a>b?a:b;
}
inline int min(reg int a,reg int b){
return a<b?a:b;
}
const int MAXN=1e6+5;
const int MAXQ=1e6+5;
const int inf=0x3f3f3f3f;
struct querys{
int id,l,r;
inline bool operator<(const querys& a)const{
return l<a.l;
}
};
inline void manacher(reg int n,reg char str[],reg int res[]){
static char s[MAXN<<1];
reg int m=0;
s[0]='$',s[++m]='#';
for(reg int i=1;i<=n;++i)
s[++m]=str[i],s[++m]='#';
s[m+1]='\0';
static int p[MAXN<<1];
for(reg int i=1,r=0,mid=0;i<=m;++i){
if(i<=r)
p[i]=min(p[(mid<<1)-i],r-i+1);
else
p[i]=1;
while(s[i-p[i]]==s[i+p[i]])
++p[i];
if(i+p[i]-1>r)
r=i+p[i]-1,mid=i;
}
p[0]=inf;
reg int top=0;
static int S[MAXN<<1];
for(reg int i=1,ptr=0;i<=n;++i){
while(ptr<(i<<1)){
if(ptr+p[ptr]-1>=(i<<1))
S[++top]=ptr;
++ptr;
}
while(S[top]+p[S[top]]-1<(i<<1))
--top;
if(S[top])
res[i]=(i<<1)-S[top]+1;
else
res[i]=inf;
}
return;
}
struct Event{
int x,l,r,val;
inline Event(void){
return;
}
inline Event(reg int x,reg int l,reg int r,reg int val):x(x),l(l),r(r),val(val){
return;
}
inline bool operator<(const Event& a)const{
return x<a.x;
}
};
namespace BIT{
inline int lowbit(reg int x){
return x&(-x);
}
int n,unit[MAXN];
inline void init(reg int s){
n=s;
//fill(unit+1,unit+n+1,0);
return;
}
inline void update(reg int id,reg int val){
for(reg int i=id;i<=n;i+=lowbit(i))
unit[i]+=val;
return;
}
inline int query(reg int id){
reg int res=0;
for(reg int i=id;i;i^=lowbit(i))
res+=unit[i];
return res;
}
inline void update(const Event& e){
update(e.l,e.val),update(e.r+1,-e.val);
return;
}
}
int n;
char s[MAXN];
int q;
int sum[MAXN];
querys qu[MAXQ];
bool ans[MAXQ];
bool ed;
int main(void){
n=read(),read(s+1),q=read();
for(reg int i=2;i<n;++i)
sum[i]=sum[i-1]+(s[i-1]==s[i+1]);
for(reg int i=1;i<=q;++i)
qu[i].id=i,qu[i].l=read(),qu[i].r=read();
sort(qu+1,qu+q+1);
static int lef[MAXN],rig[MAXN];
manacher(n,s,lef);
reverse(s+1,s+n+1);
manacher(n,s,rig);
reverse(rig+1,rig+n+1);
reg int tot=0;
static Event e[MAXN<<1];
for(reg int i=1;i<=n;++i){
reg int lx=i-lef[i]+2,rx=i;
reg int ly=i,ry=i+rig[i]-2;
lx=max(lx,1),ry=min(ry,n);
e[++tot]=Event(lx,ly,ry,1);
e[++tot]=Event(rx+1,ly,ry,-1);
}
sort(e+1,e+tot+1);
BIT::init(n);
for(reg int i=1,j=1;i<=q;++i){
while(j<=tot&&e[j].x<=qu[i].l)
BIT::update(e[j++]);
if(qu[i].l==qu[i].r)
ans[qu[i].id]=false;
else if(sum[qu[i].r-1]-sum[qu[i].l])
ans[qu[i].id]=true;
else
ans[qu[i].id]=!BIT::query(qu[i].r);
}
for(reg int i=1;i<=q;++i)
putchar(ans[i]+'0');
putchar('\n');
flush();
fprintf(stderr,"%.3lf s\n",1.0*clock()/CLOCKS_PER_SEC);
fprintf(stderr,"%.3lf MiB\n",(&ed-&st)/1048576.0);
return 0;
}