• [loj3396]novel


    建立AC自动机,记$|x$和$fail_{x}$分别表示$x$的深度(从$0$开始)和失配指针

    记$W_{x}$表示以$x$为结束节点的字符串权值和$,S_{x}=\sum_{z在(fail树中)x到根路径上}W_{z}$

    对于字符串$s_{i}$,定义$pos_{r}$表示$s_{i}[1,r]$在AC自动机上的位置,则
    $$
    g(s_{i}[l,r])=g(s_{i}[l,r))+\sum_{x在(fail树中)pos_{r}到根路径上,|x|\le r-l+1}W_{x}
    $$
    在此基础上,问题即对所有$r$求$\max_{1\le l\le r}\frac{g(s_{i}[l,r])}{r-l+1}$,并将$l$分为三类讨论:

    1. $l=1$,单独维护,转移即$g(s_{i}[1,r])=g(s_{i}[1,r))+S_{pos_{r}}$

    2. $2\le l\le r-|fail_{pos_{r}}|$,注意到后者随着$r$增加单调不降,即依次执行:

    • 全局加上$S_{fail_{pos_{r}}}$(由于$i\ge 2,x=pos_{r}$的情况无意义)
    • 对于$r-|fail_{pos_{r-1}}|\le l\le r-|fail_{pos_{r}}|$,在当前序列末尾加入$g(s_{i}[l,r])$
    • 查询$\max_{l\in [2,r-|fail_{pos_{r}}|]}\frac{g(s_{i}[l,r])}{r-l+1}$

    关于查询,二分枚举答案$k$,即判定是否存在$(l-1)k+g(s_{i}[l,r])\ge rk$

    维护前者的凸包(自变量为$k$),注意到$k$减小无意义,进而可以用指针代替二分

    关于加入的$g(s_{i}[l,r])$,记$t=s_{i}(r-|fail_{pos_{r}}|,r]$,则所求的$s_{i}[l,r]$即以$t$为后缀

    注意到$t$是某个串的前缀(即$fail_{pos_{r}}$对应位置),可以预处理出$g(t)$(参考$1.$)

    对反串建立AC自动机,在其中找到$t$所在位置,并依次加入$t$前的字符即可

    前者可以对每个串均预处理出(倒序枚举长度跳$fail$树),后者即加上对应位置的$S'_{x}$

    3. $r-|fail_{pos_{r}}|<l\le r$,可以利用$t$所对应前缀所求出的结果

    为了保证结果间的顺序,处理完前两种情况后在AC自动机上bfs一遍即可

    时间复杂度为$o(m)$,可以通过

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 200005
      4 #define M 5000005
      5 #define mod 998244353
      6 #define ll long long
      7 #define LL __int128
      8 int T,n,x,l,ql,qr,lst,Ans,inv[M],pos[M],q[M];
      9 char s[M];ll tag,val[M];vector<int>ch[N];
     10 struct Frac{
     11     ll x;int y;
     12     int get(){
     13         return x%mod*inv[y]%mod;
     14     }
     15     bool operator < (const Frac &n)const{
     16         return (LL)x*n.y<(LL)y*n.x;
     17     }
     18 }ans[M];
     19 struct AC{
     20     int V=1,dep[M],nex[M],ch[M][4];
     21     ll W[M],S[M],g[M];
     22     queue<int>q;vector<int>dfn,v[N];
     23     void add(char *s,int l,int id,int x){
     24         int k=1;v[id].push_back(1);
     25         for(int i=0;i<l;i++){
     26             int c=s[i]-'a';
     27             if (!ch[k][c])ch[k][c]=++V,dep[V]=dep[k]+1;
     28             k=ch[k][c],v[id].push_back(k);
     29         }
     30         W[k]+=x;
     31     }
     32     void build(){
     33         for(int i=0;i<4;i++){
     34             if (!ch[1][i])ch[1][i]=1;
     35             else nex[ch[1][i]]=1,q.push(ch[1][i]);
     36         }
     37         while (!q.empty()){
     38             int k=q.front();q.pop();
     39             S[k]=W[k]+S[nex[k]],g[k]+=S[k],dfn.push_back(k);
     40             for(int i=0;i<4;i++)
     41                 if (!ch[k][i])ch[k][i]=ch[nex[k]][i];
     42                 else{
     43                     nex[ch[k][i]]=ch[nex[k]][i];
     44                     g[ch[k][i]]+=g[k],q.push(ch[k][i]);
     45                 }
     46         }
     47     }
     48 }T1,T2;
     49 Frac get(int x,int y){
     50     return Frac{val[x]-val[y],y-x};
     51 }
     52 void add(int k){
     53     if (k==1)return;
     54     while ((ql<qr)&&(get(q[qr],k)<get(q[qr-1],q[qr])))qr--;
     55     q[++qr]=k;
     56 }
     57 Frac query(int k){
     58     if (ql>qr)return Frac{0,1};
     59     val[k+1]=-tag;
     60     while ((ql<qr)&&(get(q[ql],k+1)<get(q[ql+1],k+1)))ql++;
     61     return get(q[ql],k+1);
     62 }
     63 int main(){
     64     inv[0]=inv[1]=1;
     65     for(int i=2;i<M;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
     66     scanf("%d%d",&n,&T);
     67     for(int i=1;i<=n;i++){
     68         scanf("%s%d",s,&x),l=strlen(s);
     69         for(int j=0;j<l;j++)ch[i].push_back(s[j]-'a');
     70         T1.add(s,l,i,x),reverse(s,s+l),T2.add(s,l,i,x);
     71     }
     72     T1.build(),T2.build();
     73     for(int i=1;i<=n;i++){
     74         l=ch[i].size();
     75         for(int j=l,k=T2.v[i][l];j>=0;j--){
     76             while (j<T2.dep[k])k=T2.nex[k];
     77             pos[T1.v[i][j]]=k;
     78         }
     79     }
     80     for(int i=1;i<=n;i++){
     81         l=ch[i].size(),ql=1,qr=lst=0;
     82         for(int r=1;r<=l;r++){
     83             int k=T1.v[i][r];tag+=T1.S[T1.nex[k]];
     84             int k0=pos[T1.nex[k]];ll s=T1.g[T1.nex[k]];
     85             for(int j=r-T1.dep[T1.nex[k]];j>=r-T1.dep[T1.nex[lst]];j--){
     86                 k0=T2.ch[k0][ch[i][j-1]],s+=T2.S[k0],val[j]=s-tag;
     87             }
     88             for(int j=r-T1.dep[T1.nex[lst]];j<=r-T1.dep[T1.nex[k]];j++)add(j);
     89             lst=k,ans[k]=max(query(r),Frac{T1.g[k],r});
     90         }
     91     }
     92     for(int i:T1.dfn)ans[i]=max(ans[i],ans[T1.nex[i]]);
     93     for(int i=1;i<=n;i++){
     94         l=ch[i].size();Frac s=Frac{0,1};
     95         for(int j=1;j<=l;j++)s=max(s,ans[T1.v[i][j]]),Ans^=s.get();
     96         if ((!T)&&(i==1))printf("%.6f\n",1.0*s.x/s.y);
     97     }
     98     if (T)printf("%d\n",Ans);
     99     return 0;
    100 }
    View Code
  • 相关阅读:
    PowerDesigner应用02 逆向工程之导出PDM文件前过滤元数据(表、视图、存储过程等)
    PowerDesigner应用01 逆向工程之配置数据源并导出PDM文件
    CLR查找和加载程序集的方式(二) 流程图
    CLR查找和加载程序集的方式(一)
    C#控制台程序入口函数 Main(string[] args) 参数详解
    INotifyPropertyChanged 接口 CallerMemberName属性
    INotifyPropertyChanged 接口
    SQL Server 中执行Shell脚本计算本地文件的内容大小
    统计一个数据库中,无记录的表的sql语句
    SQL 性能优化 总结
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/16462777.html
Copyright © 2020-2023  润新知