前言
开始感觉很麻烦,想想其实很清真
题目相关
题目大意
给一个字符串
s
s
s,每一个位置都有一个权值
v
v
v
求有多少个子串满足其倒顺序排名等于子串权值和
数据范围
l e n ≤ 200000 , ′ a ′ ≤ s i ≤ ′ z ′ , 0 ≤ v ≤ 10000 len\le200000,'a'\le s_i\le'z',0\le v\le10000 len≤200000,′a′≤si≤′z′,0≤v≤10000
题解
考虑后缀数组(万年不写)
直接后缀排序、求height后
我们发现,对于一个后缀,我们分析以当前左端点开始的子串的情况
我们发现,左端点固定,子串长度越长,倒序的排名值下降,权值的和上升
所以对于一个左端点至多只有一个右端点满足条件
可以直接二分
我们发现,要维护权值和很方便,直接前缀和即可
维护倒序排名值,我们发现每个后缀都和上个串的
l
c
p
lcp
lcp排名值一样,剩下部分补上一个公差为
1
1
1的等差数列
直接用线段树维护,并在线段树上二分即可(因为要二分,所以其实可以只记录当前区间左端点的值和一个赋值标记)
复杂度
Θ
(
n
l
o
g
n
)
\Theta(nlogn)
Θ(nlogn)
事实证明,题解不是越长越好,我开始没想出来这题怎么做,看了一篇很长的题解很久没搞懂在说什么
代码
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
namespace fast_IO
{
const int IN_LEN=10000000,OUT_LEN=10000000;
char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void Swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
char cu=getchar();x=0;bool fla=0;
while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
if(x>=10)printe(x/10);
putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
if(x<0)putchar('-'),printe(-x);
else printe(x);
}
const int maxn=200003;
char s[maxn];
int sa[maxn],height[maxn],val[maxn],len;
namespace SA
{
int xx[maxn],yy[maxn],*x,*y,b[maxn],a[maxn],p;
int rank[maxn];
int cmp(int i,int j,int l)
{
return y[i]==y[j]&&(i+l>=len?-1:y[i+l])==(j+l>=len?-1:y[j+l]);
}
void get_SA()
{
x=xx;y=yy;
int m=26;
for(rg int i=0;i<len;i++)x[i]=a[i],b[a[i]]++;
for(rg int i=1;i<=m;i++)b[i]+=b[i-1];
for(rg int i=len-1;i>=0;i--)sa[--b[x[i]]]=i;
for(rg int k=1;k<=len;k<<=1)
{
p=-1;
for(rg int i=len-k;i<len;i++)y[++p]=i;
for(rg int i=0;i<len;i++)
if(sa[i]>=k)
y[++p]=sa[i]-k;
for(rg int i=0;i<=m;i++)b[i]=0;
for(rg int i=0;i<len;i++)b[x[y[i]]]++;
for(rg int i=1;i<=m;i++)b[i]+=b[i-1];
for(rg int i=len-1;i>=0;i--)sa[--b[x[y[i]]]]=y[i];
Swap(x,y);
p=1;
x[sa[0]]=0;
for(rg int i=1;i<len;i++)
x[sa[i]]=cmp(sa[i-1],sa[i],k)?p-1:p++;
if(p>=len)break;
m=p;
}
p=0;
for(rg int i=0;i<len;i++)rank[sa[i]]=i;
for(rg int i=0;i<len;i++)
{
if(rank[i]==0)continue;
int j=sa[rank[i]-1];
while(i+p<len&&j+p<len&&s[i+p]==s[j+p])p++;
height[rank[i]]=p;
p=max(0,p-1);
}
}
};
const int MAX=524288;
int l[MAX],r[MAX],mid[MAX];LL irt[MAX],mark[MAX];
void ini(const int root,const int ll,const int rr)
{
l[root]=ll,r[root]=rr,mid[root]=(ll+rr)>>1;
if(ll==rr)return;
ini(root<<1,ll,mid[root]),ini(root<<1|1,mid[root]+1,rr);
}
void down(const int root)
{
if(mark[root])
{
if(l[root]!=r[root])
{
irt[root<<1]=mark[root<<1]=mark[root];
irt[root<<1|1]=mark[root<<1|1]=mark[root]-mid[root]+l[root]-1;
}
mark[root]=0;
}
}
void insert(const int root,const int ll,const int rr,const LL val)
{
if(ll==l[root]&&rr==r[root])
{
irt[root]=mark[root]=val;
return;
}
down(root);
if(rr<=mid[root])insert(root<<1,ll,rr,val);
else if(ll>mid[root])insert(root<<1|1,ll,rr,val);
else insert(root<<1,ll,mid[root],val),insert(root<<1|1,mid[root]+1,rr,val-mid[root]+ll-1);
irt[root]=irt[root<<1];
}
int ef(const int root,const int rr,const int begin,const int cut)
{
if(l[root]==r[root])return irt[root]==val[l[root]+begin]-cut?l[root]+begin:-1;
down(root);
if(rr<=mid[root])return ef(root<<1,rr,begin,cut);
if(val[l[root<<1|1]+begin]-cut<=irt[root<<1|1])return ef(root<<1|1,rr,begin,cut);
return ef(root<<1,rr,begin,cut);
}
LL all;
struct seg
{
int l,r;
bool operator <(const seg&b)const{return l<b.l;}
}Q[maxn];
int ans;
int main()
{
scanf("%s",s),len=strlen(s);
for(rg int i=0;i<len;i++)read(val[i]);
for(rg int i=0;i<len;i++)SA::a[i]=s[i]-'a'+1;
SA::get_SA();
ini(1,1,len);
all=(LL)len*(len+1)/2;
for(rg int i=1;i<len;i++)all-=height[i],val[i]+=val[i-1];
for(rg int i=0;i<len;i++)
{
if(height[i]+1<=len-sa[i])insert(1,height[i]+1,len-sa[i],all),all-=len-sa[i]-height[i];
const int rr=ef(1,len-sa[i],sa[i]-1,sa[i]?val[sa[i]-1]:0);
if(rr!=-1)Q[++ans]=(seg){sa[i],rr};
}
std::sort(Q+1,Q+ans+1);
print(ans),putchar('\n');
for(rg int i=1;i<=ans;i++)print(Q[i].l+1),putchar(' '),print(Q[i].r+1),putchar('\n');
return flush(),0;
}
总结
好久没写sa了
感觉不是很难,但是不知为何一开始没理解
写的时候由于一些细节问题调了一会儿
好像可以后缀自动机转后缀树做