【题解】无畏造八强(双哈+马拉车+根号分治)
题面
无畏造八强(wwzbq)
题目背景
我亮牌子我抗塔,我和凯南贴脸打。四路经济2800,世界第一吹jb。
题目描述
你有n个字符集为小写字符的字符串$T_i$. 现在有q次询问,每次询问一个$x_i,y_i$ 对于每次询问,你应该求出:$T_,T_$中的公共的本质不同的回文子串个数。 题目会利用一些手段强制在线。
下面是一些可能有用的定义: 回文串:一个串翻转过来仍与其自己相同。 本质相同:对于两个子串,不考虑其在原串中的位置是否相同,只要长度相同并且对应位置字符相同。 本质不同:对于两个子串并不本质相同。
输入格式
第一行是两个整数n 接下来n行,第i行为一个非空字符串$T_i$ 接下来是一行一个整数 接下来q行,第i行有两个非负整数$x_i', y_i'$真正的询问为 (x_i = x_i' igoplus (ans_{i-1})), (y_i = y_i' igoplus (ans_{i-1})) $ans_i$表示第i次询问得到的答案,其中$ans_0 = 0$.保证$1 leq x_i, y_i leq n$. $igoplus$代表按位异或操作,对应c++中的^
输出格式
对于每次询问输出一个整数代表答案。
输入样例1
2 aba aaa 1 1 2
输出样例1
1
子任务
对于$20%(的数据,保证)sum |T_i|,qleq300$ 对于$40%(的数据,保证)sum |T_i|,qleq5000$ 对于另外$20%(的数据,保证)min{|T_|,|T_|}<=100$ 对于另外$20%$的数据,保证$n=3$ 对于$100%(的数据,)sum |T_i|<=10^5$
(sol)
正如标题所说的那样,双哈+马拉车+根号分治...
这题的大致思路是:对于字符串哈希,马拉车预处理所有本质不同的回文子串,哈希塞到set里面,然后根据$sum |T_i|<=10^5$进行根号分治...
其实不用回文树有点假?因为这道题字符集是$26$,加上马拉车要用的$1$都到了$27$,然而双哈希最多存$x2=19260817 imes19491001$种的不同的字符串,然而字符串可能的集合都到了至少$26{10000}$...或许这就是哈希吧...
结论1:一个串本质不同的回文子串个数是$O(n)$的
证明:假设在原有的串最后面加上一个字符c,这个字符会造成的贡献一定是以c为结尾的回文串,然而c最多产生一个新的回文串,这个回文串就是是以c的最长那个的回文串。为什么一定是最长的那个回文串?因为这个最长的回文串S一定包含了那些比S短的小回文串,这些回文串小${a}(由于被包括在S中,所以一定根据回文串的对称性,小){a}$一定在在S串中,出现了至少两次。
结论2:本质不同的回文子串一定是以某个点结尾的最长的那个回文串
证明:根据结论一和数学归纳法可以得出。
到了这一步,我们就把问题变为了:对于一个集群${S},sum |S_i|le105$,有$qle 105$组询问,问$|S_icap S_j | $的大小。
对于一组询问,我们可以枚举较小的那个集合,然后在较大的那个集合里查询是否存在,然后把答案备用...你以为这只是常数优化,其实你复杂度就突然对了。
结论3:本质不同的询问最多造成本质不同$O(nsqrt n)(种枚举(忽略那个)log$和$q$)
考虑$|S|ge sqrt n$的集合个数$le sqrt n $个...这很应该显然吧?
假设查询中有一个小于$sqrt n$的集合,枚举它,那么复杂度就是$O(sqrt n)$了。
假设询问的两个都是大于等于$sqrt n$的怎么办?那么考虑大于$sqrt n$的集合,它,这些集合大小加起来是$O(n)$的,但是由于$size > sqrt n$的集合只有$sqrt n$个,那么对于它来说,最多只有$sqrt n$次查询,那么就是$O(n sqrt n)$了。
本质上是确定一个按数据范围分做法的临界,使得两种做法的最坏复杂度一样,往往此时也是渐进意义下的最优解。
你直接拿$map$存一下询问,然后记忆化一下就好了。
其实代码调了很久,这是带注释版本
没带注释版本,记得开C++11
//@winlere https://paste.ubuntu.com/p/x6fqZWBDdp/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
using namespace std; typedef long long ll;
inline int qr(){register int ret=0,f=0;register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;}
const int maxn=1e5+5;
const int seed1=233;
const int seed2=439;
const int mod1=19260817;
const int mod2=19491007;
const char c1='z'+1;
const char c2='z'+2;
int n,q,ans;
int p[maxn<<1];
int lef[maxn<<1];
int ha1[maxn<<1];
int ha2[maxn<<1];
int sav1[maxn<<1];
int sav2[maxn<<1];
map < pair < int , int > , int > que;
multiset < pair < int , int > > s[maxn];
inline int ksm(int base,const int&p,const int&mod){
register int ret=1;
for(register int t=p;t;t>>=1,base=1ll*base*base%mod)
if(t&1) ret=1ll*ret*base%mod;
return ret;
}
inline pair < int , int > get_ha(const int&l,const int&r){
return make_pair((ha1[r]-1ll*ha1[l-1]*sav1[r-l+1]%mod1+mod1)%mod1,(ha2[r]-1ll*ha2[l-1]*sav2[r-l+1]%mod2+mod2)%mod2);
}
inline int Min(const int&a,const int&b){
if(a<b) return a;
return b;
}
inline void find_pam(const int&num){
auto&S=s[num];
string now;
now.push_back(c1);
register char c=getchar();
while(c<'a'||c>'z') c=getchar();
while(c>='a'&&c<='z') now.push_back(c2),now.push_back(c),c=getchar();
for(register int t=1,edd=now.size()-1;t<=edd;++t){
ha1[t]=(1ll*ha1[t-1]*seed1%mod1+now[t]-'a'+1ll)%mod1;
ha2[t]=(1ll*ha2[t-1]*seed2%mod2+now[t]-'a'+1ll)%mod2;
}
for(register int t=1,edd=now.size();t<edd;++t)
p[t]=0,lef[t]=0x3f3f3f3f;
register int mid=0,r=0;
for(register int t=1,edd=now.size();t<edd;++t){
if(t<=r) p[t]=Min(r-t+1,p[(mid<<1)-t]);
lef[t+p[t]-1]=Min(lef[t+p[t]-1],t-p[t]+1);
while(now[t-p[t]]==now[t+p[t]]) lef[t+p[t]]=Min(lef[t+p[t]],t-p[t]),++p[t];
if(t+p[t]-1>r) mid=t,r=t+p[t]-1;
}
for(register int t=1,edd=now.size()-1;t<=edd;++t){
if(lef[t]>t||now[t]=='|') continue;
register auto t1=get_ha(lef[t],t);
if(S.find(t1)==S.end()) S.insert(t1);
}
}
int main(){
n=qr();
for(register int t=0,edd=maxn<<1;t<edd;++t) sav1[t]=ksm(seed1,t,mod1),sav2[t]=ksm(seed2,t,mod2);
for(register int t=1;t<=n;++t) find_pam(t);
q=qr();
for(register int t=1,t1,t2,t3=0;t<=q;++t,t3=0){
t1=ans^qr();t2=ans^qr();
if(t1>t2) swap(t1,t2);
register auto temp=make_pair(t1,t2);
if(que.find(temp)!=que.end())
printf("%d
",ans=que[temp]);
else {
if(s[t1].size()>s[t2].size()) swap(t1,t2);
for(register auto f:s[t1])
if(s[t2].find(f)!=s[t2].end()) ++t3;
que.insert(make_pair(temp,ans=t3));
printf("%d
",ans);
}
}
return 0;
}