「九省联考 2018」制胡窜
苟题目,搞了我一天。
显然要搞一个SAM,然后搞一个线段树合并,关于定位询问串搞一个树上倍增
然后你考虑一个细节贼多的分类讨论
应该是可以不求补集的,我最开始一直这么想但是有个东西不会维护后来发现是可以维护的...
但是补集应该简单一点吧...?
就是每次切两刀要把所有位置的刀切开,这么多细节我肯定懒得说。
说下我错过的(如果你写法和我类似)
注意左边的到切的是[,),右边的切的是(,]
然后就是讨论讨论讨论
Code:
#include
#include
#include
#include
#define ll long long
using std::min;
using std::max;
const int N=2e5+10;
template
void read(T &x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
int root[N];
namespace seg
{
int ch[N*50][2],yuul[N*50],yuur[N*50],tot;
ll s1[N*50],s2[N*50];
#define ls ch[now][0]
#define rs ch[now][1]
void updata(int now)
{
yuul[now]=yuul[ls]?yuul[ls]:yuul[rs];
yuur[now]=yuur[rs]?yuur[rs]:yuur[ls];
s1[now]=s1[ls]+s1[rs];
s2[now]=s2[ls]+s2[rs];
int r1=yuur[ls],r2=yuul[rs];
if(r1&&r2)
{
s1[now]+=1ll*(r2-r1)*r2;
s2[now]+=(r2-r1);
}
}
int qryex(int now,int L,int R,int l,int r)//有没有点..
{
if(!now) return 0;
if(l==L&&r==R) return 1;
int Mid=L+R>>1;
if(r<=Mid) return qryex(ls,L,Mid,l,r);
else if(l>Mid) return qryex(rs,Mid+1,R,l,r);
else return qryex(ls,L,Mid,l,Mid)||qryex(rs,Mid+1,R,Mid+1,r);
}
void qrys(int now,int L,int R,int l,int r,ll &ret1,ll &ret2)
{
if(!now) return;
if(l==L&&r==R)
{
ret1+=s1[now],ret2+=s2[now];
return;
}
int Mid=L+R>>1;
if(r<=Mid) qrys(ls,L,Mid,l,r,ret1,ret2);
else if(l>Mid) qrys(rs,Mid+1,R,l,r,ret1,ret2);
else
{
qrys(ls,L,Mid,l,Mid,ret1,ret2),qrys(rs,Mid+1,R,Mid+1,r,ret1,ret2);
int r1=yuur[ls],r2=yuul[rs];
if(r1&&r2&&r1>=l&&r2<=r)
{
ret1+=1ll*(r2-r1)*r2;
ret2+=(r2-r1);
}
}
}
//qryri,小于等于某位置最右
//qryle,大于等于某位置最左
int qryri(int now,int l,int r,int p)
{
if(r==p) return yuur[now];
int mid=l+r>>1;
if(p<=mid) return qryri(ls,l,mid,p);
else return max(yuur[ls],qryri(rs,mid+1,r,p));
}
int qryle(int now,int l,int r,int p)
{
if(l==p) return yuul[now];
int mid=l+r>>1;
if(p<=mid)
{
int ret=qryle(ls,l,mid,p);
if(ret) return ret;
else return yuul[rs];
}
else return qryle(rs,mid+1,r,p);
}
void ins(int &now,int l,int r,int p)
{
if(!now) now=++tot;
if(l==r)
{
yuul[now]=yuur[now]=l;
return;
}
int mid=l+r>>1;
if(p<=mid) ins(ls,l,mid,p);
else ins(rs,mid+1,r,p);
updata(now);
}
int Merge(int x,int y,int l,int r)
{
if(!x||!y) return x^y;
if(l==r) return x;
int mid=l+r>>1,now=++tot;
ls=Merge(ch[x][0],ch[y][0],l,mid);
rs=Merge(ch[x][1],ch[y][1],mid+1,r);
updata(now);
return now;
}
}
ll gets(int n){return 1ll*n*(n+1)/2;}
using seg::ins;
using seg::Merge;
using seg::qryex;
using seg::qryri;
using seg::qryle;
using seg::qrys;
int n,q;
char s[N];
int ch[N][10],len[N],par[N],f[19][N],pos[N],tax[N],A[N],tot=1,las=1;
void extend(int c,int pos)
{
int now=++tot,p=las;
ins(root[now],1,n,pos);
len[now]=len[p]+1;
while(p&&!ch[p][c]) ch[p][c]=now,p=par[p];
if(!p) par[now]=1;
else
{
int x=ch[p][c];
if(len[x]==len[p]+1) par[now]=x;
else
{
int y=++tot;
len[y]=len[p]+1;
par[y]=par[x];
memcpy(ch[y],ch[x],sizeof ch[y]);
while(p&&ch[p][c]==x) ch[p][c]=y,p=par[p];
par[now]=par[x]=y;
}
}
las=now;
}
int clim(int now,int d)
{
for(int i=18;~i;i--)
if(len[f[i][now]]>=d)
now=f[i][now];
return now;
}
//qryri,小于等于某位置最右
//qryle,大于等于某位置最左
ll work(int now,int d)
{
if(d==1) return 0;
int lp=qryri(root[now],1,n,n)-d+1;//最右边点的左端点
int rp=qryle(root[now],1,n,1);//最左边点的右端点
if(qryex(root[now],1,n,rp+d,lp-1)) return 0;//有3个互不香蕉
ll ret=0;
if(rp<=lp)
{
int s=qryle(root[now],1,n,lp);//开始的右端点位置
int t=qryri(root[now],1,n,rp+d-1);//结束的右端点位置
if(s>t) return (rp-(t-d+1))*(s-lp);
int nxts=s;//当前的右端点
s=qryri(root[now],1,n,s-1);//前一个右端点
if(s) ret+=1ll*(nxts-s)*(nxts-lp);
ll ret1=0,ret2=0;
qrys(root[now],1,n,nxts,t,ret1,ret2);
ret+=ret1-ret2*lp;
int nxtt=qryle(root[now],1,n,t+1);//后一个右端点
ret+=1ll*(rp-(t-d+1))*(nxtt-lp);
return ret;
}
ret+=1ll*(rp-d)*(rp-lp);//i不切,j全切
ret+=1ll*(rp-lp)*(n-1)-(gets(rp-1)-gets(lp-1));
int s=rp,t=lp+d-1;
ll ret1=0,ret2=0;
qrys(root[now],1,n,s,t,ret1,ret2);
ret+=ret1-ret2*lp;
//int nxts=qryle(root[now],1,n,s+1);
//ret+=(nxts-rp)*(nxts-lp+1);
return ret;
}
int main()
{
read(n),read(q);
scanf("%s",s+1);
for(int i=1;i<=n;i++) extend(s[i]-'0',i),pos[i]=las;
for(int i=1;i<=tot;i++) ++tax[len[i]];
for(int i=1;i<=n;i++) tax[i]+=tax[i-1];
for(int i=1;i<=tot;i++) A[tax[len[i]]--]=i;
for(int i=tot;i;i--)
{
int now=A[i],p=par[now];
f[0][now]=p;
root[p]=Merge(root[p],root[now],1,n);
}
for(int i=1;i<=tot;i++)
{
int now=A[i];
for(int j=1;f[j-1][now];j++) f[j][now]=f[j-1][f[j-1][now]];
}
ll ans=1ll*(n-1)*(n-2)/2;
for(int l,r,d,i=1;i<=q;i++)
{
read(l),read(r),d=r+1-l;
int now=clim(pos[r],d);
printf("%lld\n",ans-work(now,d));
}
return 0;
}
2019.3.19