链接
题解
出题人的题解
个人认为这个题解写得相当清晰,我就不写了。
笔记
- 这个程序要写三个数据结构。有点难处理。
- 本质是要看清这三个数据结构分别要干什么事情。
- 线段树:我没有维护区间等差数列,而是在每个位置记录了以该位置为左端点有多少应该被统计的字符串。因为LCT上每次修改的是把连续长度、统计位置的右端点连续的字符串右端点统一移到另一个地方,所以我们只需要支持区间加减1和区间求和就行了。
- LCT:本质是要维护每个点代表的那一系列的字符串的最右出现位置并方便修改。所以LCT可以适当弱化一下功能————大可以把SAM或者后缀树建完之后再link出LCT。此时的link只用支持一个认父亲功能就行了;而cut则根本不需要。以及access每次找到当前这棵splay代表的长度的区间然后在线段树上区间加加减减就好了。
- SAM:这就是个工具人,建出parenttree或者说反串后缀树之后就没有用了。他就是为LCT提供一个合理的树形结构。
代码
#include<bits/stdc++.h>
#define LL long long
#define MAXN 101000
#define MAXM 201000
using namespace std;
template<typename T> void Read(T &cn)
{
char c; int sig = 1;
while(!isdigit(c = getchar())) if(c == '-') sig = 0;
if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
else {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template<typename T> void Write(T cn)
{
int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
while(cn)cm = cm*10+cn%10,cn/=10,wei++;
while(wei--)putchar(cm%10+48),cm/=10;
putchar(cx+48);
}
template<typename T> void WriteL(T cn) {Write(cn); puts(""); }
template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); }
template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
struct Seg{
struct node{
LL p, he;
int len;
void qing(int cn) {len = cn; p = he = 0; }
void zeng(int cn) {p = p+cn; he = he+len*cn; }
};
node t[MAXN*4+1];
int n;
void build(int cn, int l, int r)
{
t[cn].qing(r-l+1); if(l == r) return;
int zh = (l+r)>>1; build(cn<<1,l,zh); build((cn<<1)|1,zh+1,r);
}
void jia(int cn, int cl, int cr, int cm, int l, int r)
{
if(cl <= l && r <= cr) {t[cn].zeng(cm); return; }
tui(cn); int zh = (l+r)>>1;
if(cl <= zh) jia(cn<<1,cl,cr,cm,l,zh);
if(cr > zh) jia((cn<<1)|1,cl,cr,cm,zh+1,r);
update(cn);
}
LL qiu(int cn, int cl, int cr, int l, int r)
{
if(cl <= l && r <= cr) return t[cn].he;
tui(cn); LL guo = 0; int zh = (l+r)>>1;
if(cl <= zh) guo = guo+qiu(cn<<1,cl,cr,l,zh);
if(cr > zh) guo = guo+qiu((cn<<1)|1,cl,cr,zh+1,r);
return guo;
}
void tui(int cn) {t[cn<<1].zeng(t[cn].p); t[(cn<<1)|1].zeng(t[cn].p); t[cn].p = 0; }
void update(int cn) {t[cn].he = t[cn<<1].he + t[(cn<<1)|1].he; }
void build(int cn) {n = cn; build(1,0,n); }
void jia(int cl, int cr, int cm)
{
// printf(" in jia : (%d,%d) %d
",cl,cr,cm);
Max(cl,0); Max(cr,0); jia(1,cl,cr,cm,0,n);
}
LL qiu(int cl, int cr) {return qiu(1,cl,cr,0,n); }
}T;
struct LCT{
struct node{
int fa, ch[2];
int endpos, da, len;
void qing(int cn = 0) {fa = ch[0] = ch[1] = 0; len = da = cn; endpos = 0; }
void bian(int cn) {endpos = cn; }
};
node t[MAXN*2+1];
int tlen;
int zhan[MAXN*2+1], zlen;
int isro(int cn) {return t[t[cn].fa].ch[0] != cn && t[t[cn].fa].ch[1] != cn; }
void pdown(int cn) {t[t[cn].ch[0]].bian(t[cn].endpos); t[t[cn].ch[1]].bian(t[cn].endpos); }
void update(int cn) {t[cn].da = max(max(t[t[cn].ch[0]].da, t[t[cn].ch[1]].da), t[cn].len); }
void rotate(int cn)
{
int fa = t[cn].fa, zu = t[fa].fa, ch1 = t[fa].ch[1] == cn, ch2 = t[zu].ch[1] == fa, hai = t[cn].ch[ch1^1];
if(!isro(fa)) t[zu].ch[ch2] = cn; t[cn].fa = zu;
t[cn].ch[ch1^1] = fa; t[fa].fa = cn;
t[fa].ch[ch1] = hai; t[hai].fa = fa;
update(fa); update(cn);
}
void splay(int cn)
{
if(!cn) return;
zlen = 0; int cm = cn;
while(!isro(cm)) cm = t[zhan[++zlen] = cm].fa; zhan[++zlen] = cm;
for(int i = zlen;i>=1;i--) pdown(zhan[i]);
while(!isro(cn)) {if(!isro(t[cn].fa)) rotate(t[cn].fa); rotate(cn); }
}
void access(int cn, int cm)
{
// printf("in access : cn = %d cm = %d
",cn,cm);
for(int i = 0;cn;cn = t[i = cn].fa)
{
// printf(" cn = %d i = %d
",cn,i);
splay(cn); splay(t[cn].fa);
int len1 = t[cn].len, len2 = t[t[cn].fa].len+1;
// printf(" len1 = %d len2 = %d
",len1,len2);
if(len2 <= len1) T.jia(t[cn].endpos-len1+1, t[cn].endpos-len2+1, -1);
pdown(cn); t[cn].endpos = cm;
if(len2 <= len1) T.jia(t[cn].endpos-len1+1, t[cn].endpos-len2+1, 1);
t[cn].ch[1] = i; update(cn);
}
}
void link_w(int cn, int fa, int clen) {t[cn].fa = fa; t[cn].len = t[cn].da = clen; }
void build(int cn) {tlen = cn; for(int i = 1;i<=cn;i++) t[i].qing(); }
}LCT;
struct SAM{
struct node{
int ch[26], link, len;
void qing() {memset(ch,0,sizeof(ch)); link = len = 0; }
};
node t[MAXN*2+1];
int tlen, last;
int pos[MAXN+1];
void build() {tlen = last = 1; t[1].qing(); }
void jia(int cn, int cm)
{
int cur = ++tlen; t[cur].qing(); pos[cm] = cur;
t[cur].len = t[last].len+1;
int p = last;
for(;p && !t[p].ch[cn];p = t[p].link) t[p].ch[cn] = cur;
if(!p) t[cur].link = 1;
else {
int q = t[p].ch[cn];
if(t[q].len == t[p].len+1) t[cur].link = q;
else {
int cln = ++tlen; t[cln] = t[q];
t[cln].len = t[p].len+1;
t[q].link = t[cur].link = cln;
for(;p && t[p].ch[cn] == q;p = t[p].link) t[p].ch[cn] = cln;
}
}
last = cur;
}
void outit()
{
for(int i = 1;i<=tlen;i++)
{
printf("t[%d].link = %d .len = %d
",i,t[i].link,t[i].len);
for(int j = 0;j<=25;j++) if(t[i].ch[j]) printf(" .ch[%c] = %d
",j+'a',t[i].ch[j]);
}
}
void pre() {LCT.build(tlen); for(int i = 2;i<=tlen;i++) LCT.link_w(i, t[i].link, t[i].len); }
void gai(int cn) {LCT.access(pos[cn], cn); }
}S;
struct xunwen{
int l, r, pos;
void getit(int cn) {Read(l); Read(r); pos = cn; }
inline friend bool operator <(xunwen a, xunwen b) {return a.r == b.r ? a.l < b.l : a.r < b.r; }
};
int n,q;
xunwen a[MAXM+1];
char c[MAXN+1];
LL ans[MAXM+1];
void getit(char c[], int &clen)
{
while(!isalpha(c[1] = getchar())); clen = 1;
while(isalpha(c[++clen] = getchar())); clen--;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
getit(c, n);
Read(q);
for(int i = 1;i<=q;i++) a[i].getit(i);
sort(a+1,a+q+1);
S.build();
for(int i = 1;i<=n;i++) S.jia(c[i]-'a',i);
S.pre(); T.build(n);
int xian = 0;
for(int i = 1;i<=n;i++)
{
S.gai(i);
while(xian < q && a[xian+1].r == i) xian++, ans[a[xian].pos] = T.qiu(a[xian].l,a[xian].r);
}
for(int i = 1;i<=q;i++) WriteL(ans[i]);
return 0;
}