题目大意:给出一个字符串,对于它的每个前缀S,求出S所有子串(不要求本质不同)的所有前缀在border树上的(深度-1)的和。例如
,对于字符串abab,它的第四个前缀S4=abab,有子串a,b,a,b,ab,ba,ab,aba,bab,abab,他们的所有前缀在border树上的(深度-1)的和分别是0,0,0,0,0,0,0,1,1,2,因此答案是4。

对于一个串S,它的所有前缀在border树上的(深度-1)的和,等于它的所有前缀在S中出现次数减去|S|。因为考虑任意一个前缀和这个前缀在后面某个位置出现的位置,必然存在一个border让其产生1的贡献。

对于一个串S,它的所有子串的所有前缀在border树上的(深度-1)的和,等于对于所有本质不同的字符串S,$\tbinom{cnt_S}{2}$之和,其中$cnt_S$表示S的出现次数。因为考虑任意两个位置不同的相同子串,必然存在一个border让其产生1的贡献。

因此,暴力的方法是对于每一个前缀,建立后缀树,并在每个节点上统计$\tbinom{sum_u}{2}*(len_{u}-len_{fa})$的和。

考虑每次加一个字符的过程,相当于是选择一条从一个点到根的路径,将路径上所有点的sum加一,而答案仅仅是一个关于x的二次函数。我们对于每个点维护$f(x)=ax^2+bx+c$,并规定最终的答案为$x=0$时$f(x)$的值。显然路径加满足结合律,用树剖或lct即可。

  1 #include<bits/stdc++.h>
  2 #define mod 1000000007
  3 #define G2 500000004
  4 using namespace std;
  5 typedef long long int ll;
  6 const int maxn=3E5+5;
  7 int n;
  8 string str;
  9 ll ans;
 10 namespace SAM
 11 {
 12     struct node
 13     {
 14         int fa,len,ch[26];
 15     }t[maxn*2];
 16     int tot=1,last=1;
 17     int where[maxn],sum[maxn],minPos[maxn];
 18     inline void add(int x,int id)
 19     {
 20         int now=++tot,u=last;
 21         t[now].len=t[u].len+1;
 22         last=tot;
 23         where[id]=now;
 24         minPos[now]=id;
 25         for(;u&&!t[u].ch[x];u=t[u].fa)
 26             t[u].ch[x]=now;
 27         if(!u)
 28             t[now].fa=1;
 29         else
 30         {
 31             int v=t[u].ch[x];
 32             if(t[v].len==t[u].len+1)
 33                 t[now].fa=v;
 34             else
 35             {
 36                 int w=++tot;
 37                 t[w]=t[v];
 38                 t[w].len=t[u].len+1;
 39                 t[now].fa=t[v].fa=w;
 40                 for(;u&&t[u].ch[x]==v;u=t[u].fa)
 41                     t[u].ch[x]=w;
 42             }
 43         }
 44     }
 45 }
 46 struct info
 47 {
 48     ll a,b,c;
 49     info(ll A=0,ll B=0,ll C=0):a(A),b(B),c(C){};
 50     info operator*(const int&d)
 51     {
 52         info A;
 53         A.a=a;
 54         A.b=(b+a*d*2%mod)%mod;
 55         A.c=((a*d+b)%mod*d%mod+c)%mod;
 56         return A;
 57     }
 58     info operator+(const info&A)
 59     {
 60         info B;
 61         B.a=(a+A.a)%mod;
 62         B.b=(b+A.b)%mod;
 63         B.c=(c+A.c)%mod;
 64         return B;
 65     }
 66 };
 67 namespace LCT
 68 {
 69     int fa[maxn],son[maxn][2];
 70     info ans[maxn],val[maxn];
 71     int tag[maxn];
 72     inline bool notroot(int x)
 73     {
 74         return son[fa[x]][0]==x||son[fa[x]][1]==x;
 75     }
 76     inline void pushup(int x)
 77     {
 78         ans[x]=ans[son[x][0]]+ans[son[x][1]]+val[x];
 79     }
 80     inline void rotate(int x)
 81     {
 82         int y=fa[x];
 83         int c=son[y][0]==x;
 84         fa[x]=fa[y];
 85         son[y][!c]=son[x][c];
 86         if(son[x][c])
 87             fa[son[x][c]]=y;
 88         if(notroot(y))
 89             son[fa[y]][son[fa[y]][1]==y]=x;
 90         son[x][c]=y;
 91         fa[y]=x;
 92         pushup(y);
 93         pushup(x);
 94     }
 95     inline void put(int x,const int&d)
 96     {
 97         tag[x]+=d;
 98         val[x]=val[x]*d;
 99         ans[x]=ans[x]*d;
100     }
101     inline void pushdown(int x)
102     {
103         int d=tag[x]; 
104         tag[x]=0;
105         put(son[x][0],d),put(son[x][1],d);
106     }
107     void down(int x)
108     {
109         if(!notroot(x))
110         {
111             pushdown(x);
112             return;
113         }
114         down(fa[x]);
115         pushdown(x);
116     }
117     inline void splay(int x)
118     {
119         down(x);
120         while(notroot(x))
121         {
122             int y=fa[x];
123             if(!notroot(y))
124                 rotate(x);
125             else if((son[fa[y]][1]==y)==(son[y][1]==x))
126                 rotate(y),rotate(x);
127             else
128                 rotate(x),rotate(x);
129         }
130     }
131     ll now;
132     inline ll add(int u)
133     {
134         splay(u);
135         son[u][1]=0;
136         pushup(u);
137         while(fa[u])
138         {
139             int y=fa[u];
140             splay(y);
141             son[y][1]=u;
142             pushup(y);
143             u=y;
144         }
145         splay(u);
146         ll x=ans[u].c;
147         put(u,1);
148         now=(now+ans[u].c-x+mod)%mod;
149         return now;
150     }
151     inline void init()
152     {
153         using namespace SAM;
154         for(int i=1;i<=SAM::tot;++i)
155         {
156             ll d=(t[i].len-t[t[i].fa].len);
157             ans[i]=val[i]=info(G2*d%mod,(mod-G2)*d%mod,0);
158             fa[i]=t[i].fa;
159         }
160     }
161 }
162 int main()
163 {
164     ios::sync_with_stdio(false);
165     cin>>n>>str;
166     for(int i=0;i<n;++i)
167         SAM::add(str[i]-'a',i);
168     LCT::init();
169     ll tot=0;
170     for(int i=0;i<n;++i)
171     {
172         int pos=SAM::where[i];
173         ans=((ans+LCT::add(pos)%mod+mod)%mod+mod)%mod;
174         cout<<ans<<'\n';
175     }
176     return 0;
177 }
View Code

 

 posted on 2021-02-25 21:52  GreenDuck  阅读(69)  评论(0编辑  收藏  举报