[bzoj5405]platform

7 篇文章 1 订阅
3 篇文章 0 订阅

前言

开始感觉很麻烦,想想其实很清真

题目相关

题目链接

题目大意

给一个字符串 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 len200000asiz0v10000

题解

考虑后缀数组(万年不写
直接后缀排序、求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了
感觉不是很难,但是不知为何一开始没理解
写的时候由于一些细节问题调了一会儿
好像可以后缀自动机转后缀树做

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值