白兔有一个长度为?的字符串。
白云有?个询问,每个询问会询问一段区间的本质不同回文子串个数。
挺好玩的题
先说一个暴力的做法,就是建立PAM
然后离线, r r r端点一个一个加,维护l的答案
然后每一次,就暴力找到以 r r r结尾的所有回文串,再找到上一次出现的位置,这个可以在PAM的fail树上用线段树维护,然后用树状数组资瓷区间加即可
那么复杂度就是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)的
考虑优化,每一次加入所有字符串太慢了
有一个结论是
以 r r r结尾的回文串,可以被分成 l o g log log段等差数列
具体思想来自:金策_字符串算法选讲
但是我个人感觉这题最主要的结论不是这个
我们可以用同样的思想
对于每一个回文串分组,也是按照 2 i 2^i 2i来分组
每一组里面,你会发现,他们的区间加可以合在一起,并成一段连续的区间
那么我们只需要暴力遍历每log组,每次修改就可以了
至于为什么可以合在一起,因为他们上一次出现的位置恰好就是同一组里面的上一个
因为他们的长度都不相差一倍
于是对于每一组,暴力做就OK了
于是复杂度就是的 n l o g 2 n nlog^2n nlog2n了
em…学习了 2 i 2^i 2i分组的思想以及PAM的姿势
#include
#include
#include
#include
#include
using namespace std;
typedef pair<int,int> PI;
typedef long long LL;
const int N=300005;
const int M=1000005;
const int MOD=1e9+7;
int add (int x,int y) {x=x+y;return x>=MOD?x-MOD:x;}
int mul (int x,int y) {return (LL)x*y%MOD;}
int dec (int x,int y) {x=x-y;return x<0?x+MOD:x;}
int Pow (int x,int y)
{
if (y==1) return x;
int lalal=Pow(x,y>>1);
lalal=mul(lalal,lalal);
if (y&1) lalal=mul(lalal,x);
return lalal;
}
int n,m;
char ss[N];
struct qq
{
int son[N][26];
int fail[N];
int len[N];
int s[N],num,n,last;
int d[N],f[N];
void Init () {num=1;fail[0]=1;len[1]=-1;len[0]=0;s[0]=-1;}
int find (int x,int xx)
{
while (s[xx-len[x]-1]!=s[xx]) x=fail[x];
return x;
}
void Ins (int x)
{
s[++n]=x;
int now=find(last,n);
//printf("YES:%d %d %d %d\n",now,x,s[n],n-len[now]-1);
if (son[now][x]==0)
{
num++;
fail[num]=son[find(fail[now],n)][x];
son[now][x]=num;
len[num]=len[now]+2;
//printf("NO:%d %d\n",now,num);
d[num]=len[num]-len[fail[num]];
if (d[num]==d[fail[num]]) f[num]=f[fail[num]];
else f[num]=num;
}
//printf("YES:%d\n",fail[num]);
last=son[now][x];
}
vector<int> vec[N];
void link ()
{
for (int u=0;u<=num;u++)
if (u!=1)
vec[fail[u]].push_back(u);
}
int L[N],R[N],id;
void dfs (int x)
{
L[x]=++id;
int siz=vec[x].size();
for (int u=0;u<siz;u++) dfs(vec[x][u]);
R[x]=id;
}
}PAM;
struct qt
{
int l,r;
int s1,s2;
int mx;
}tr[N*2];int num;
void update (int now)
{
int s1=tr[now].s1,s2=tr[now].s2;
tr[now].mx=max(tr[s1].mx,tr[s2].mx);
}
void bt (int l,int r)
{
int now=++num;
tr[now].l=l;tr[now].r=r;
if (l==r) return ;
int mid=(l+r)>>1;
tr[now].s1=num+1;bt(l,mid);
tr[now].s2=num+1;bt(mid+1,r);
update(now);
}
int query (int now,int l,int r)
{
if (tr[now].l==l&&tr[now].r==r) return tr[now].mx;
int mid=(tr[now].l+tr[now].r)>>1;
int s1=tr[now].s1,s2=tr[now].s2;
if (r<=mid) return query(s1,l,r);
else if (l>mid) return query(s2,l,r);
else return max(query(s1,l,mid),query(s2,mid+1,r));
}
void change (int now,int x,int id)
{
if (tr[now].l==tr[now].r) {tr[now].mx=id;return ;}
int s1=tr[now].s1,s2=tr[now].s2;
int mid=(tr[now].l+tr[now].r)>>1;
if (x<=mid) change(s1,x,id);
else change(s2,x,id);
update(now);
}
int f[N];
int lb (int x) {return x&(-x);}
void modify (int x,int c) {while (x<=n){f[x]+=c;x+=lb(x);}}
int get (int x) {int lalal=0;while (x>0){lalal=lalal+f[x];x-=lb(x);}return lalal;}
vector<PI> Q[N];
int main()
{
PAM.Init();
scanf("%d%d",&n,&m);
scanf("%s",ss+1);
for (int u=1;u<=n;u++) PAM.Ins(ss[u]-'a');
/*for (int u=1;u<=PAM.num;u++)
printf("%d %d\n",PAM.len[u],PAM.fail[u]);*/
PAM.link();
PAM.dfs(1);
bt(1,PAM.id);
for (int u=1;u<=m;u++)
{
int l,r;
scanf("%d%d",&l,&r);
Q[r].push_back({u,l});
}
int now=1;
int ans=0;
for (int u=1;u<=n;u++)
{
now=PAM.son[PAM.find(now,u)][ss[u]-'a'];
//printf("%d\n",now);
for (int i=now;i!=0;i=PAM.fail[PAM.f[i]])
{
int t=query(1,PAM.L[i],PAM.R[i]);
modify(max(1,t-PAM.len[i]+2),1);
modify(u-PAM.len[PAM.f[i]]+2,-1);
}
change(1,PAM.L[now],u);
int siz=Q[u].size();
for (int i=0;i<siz;i++)
{
//printf("%d %d %d\n",Q[u][i].first,Q[u][i].second,get(Q[u][i].second));
ans=add(ans,mul(get(Q[u][i].second),Q[u][i].first));
}
}
printf("%d\n",ans);
return 0;
}