• 【题解】无畏造八强(双哈+马拉车+根号分治)


    【题解】无畏造八强(双哈+马拉车+根号分治)

    1814 Clarke and string

    题面

    无畏造八强(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;
    }
    
    
  • 相关阅读:
    JQUERY
    五分钟技术演讲
    T-SQL基础--chp10可编程对象学习笔记[下]
    SQL中用SET赋值和用SELECT赋值的区别。
    Can't find file: './mysql/plugin.frm' (errno: 13)[mysql数据目录迁移错位]错误解决
    jetty属性
    JRebel 5.3.2
    vue+mongoodb+node连接数据库
    css样式
    超出文本显示一行文字
  • 原文地址:https://www.cnblogs.com/winlere/p/10940617.html
Copyright © 2020-2023  润新知