zoukankan      html  css  js  c++  java
  • bzoj 4516: 生成魔咒 后缀数组

    题目大意

    在结尾动态插入字符,每次插入结束后输出当前串中本质不同的字串个数

    题解

    注意一开始是空串,然后我们我们可以打表观察规律
    我们发现一直在开头插入字符和一直在结尾插入字符得到的答案是一样的
    所以我们从开头插入字符
    那么每次我们相于插入了一个后缀
    这样就多了n-sa[i]个前缀
    但是这些前缀中有重复的
    所以我们要在已经插入的后缀中找出与之最长的lcp长度
    减去这个长度就是我们得到的不同的字串个数了
    由于求lcp时是对height一直取min
    所以我们找最长的lcp是只需要找所有已经计算的了后缀中,rank最接近当前加入的后缀的rank的两个后缀即可
    复杂度(O(nlogn))

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(ll &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    inline ll cat_max(const ll &a,const ll &b){return a>b ? a:b;}
    inline ll cat_min(const ll &a,const ll &b){return a<b ? a:b;}
    const ll maxn = 100010;
    ll wa[maxn],wb[maxn],ws[maxn];
    ll rank[maxn],height[maxn],sa[maxn];
    inline bool cmp(ll *r,ll i,ll j,ll k){
    	return r[i] == r[j] && r[i+k] == r[j+k];
    }
    void da(ll *r,ll n,ll m){
    	ll i,j,p,*x = wa,*y = wb;
    	for(i=0;i<m;++i) ws[i] = 0;
    	for(i=0;i<n;++i) ws[x[i] = r[i]]++;
    	for(i=1;i<m;++i) ws[i] += ws[i-1];
    	for(i=n-1;i>=0;--i) sa[--ws[x[i]]] = i;
    	for(j=1,p=1;p<n;j<<=1,m=p){
    		for(p=0,i=n-j;i<n;++i) y[p++] = i;
    		for(i=0;i<n;++i) if(sa[i] >= j) y[p++] = sa[i] - j;
    		for(i=0;i<m;++i) ws[i] = 0;
    		for(i=0;i<n;++i) ws[x[y[i]]]++;
    		for(i=1;i<m;++i) ws[i] += ws[i-1];
    		for(i=n-1;i>=0;--i) sa[--ws[x[y[i]]]] = y[i];
    		for(swap(x,y),p=1,i=1,x[sa[0]] = 0;i<n;++i)
    		x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1 : p++;
    	}
    }
    inline void get_h(ll *r,ll n){
    	ll i,j,k=0;for(i=1;i<=n;++i) rank[sa[i]] = i;
    	for(i=0;i<n;height[rank[i++]] = k)
    	for(k ? --k : 0,j = sa[rank[i]-1];r[i+k] == r[j+k];++k);
    }
    ll loger[maxn],minn[maxn][22],n;
    inline void pre(){
    	loger[1] = 0;
    	for(ll i=2;i<=n;++i){
    		loger[i] = loger[i-1];
    		if( (1 << loger[i]+1) == i) ++loger[i];
    	}
    	for(ll i=n;i>=1;--i){
    		minn[i][0] = height[i];
    		for(ll j=1;i+(1<<j)-1<=n;++j){
    			minn[i][j] = min(minn[i][j-1],minn[i+(1<<j-1)][j-1]);
    		}
    	}
    }
    inline ll lcp(ll x,ll y){
    	if(x+1 > y) swap(x,y);++x;
    	ll k = loger[y-x+1];
    	return min(minn[x][k],minn[y-(1<<k)+1][k]);
    }
    ll a[maxn],b[maxn];
    ll pos_min[maxn<<2],pos_max[maxn<<2],M;
    inline void modify(ll x,ll y){
    	for(pos_min[x+=M]=y,pos_max[x]=y,x>>=1;x;x>>=1){
    		pos_min[x] = min(pos_min[x<<1],pos_min[x<<1|1]);
    		pos_max[x] = max(pos_max[x<<1],pos_max[x<<1|1]);
    	}
    }
    inline ll query_min(ll s,ll t){
    	if(s > t) return -1;
    	ll ret = pos_min[0];
    	for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
    		if(~s&1) ret = min(ret,pos_min[s^1]);
    		if( t&1) ret = min(ret,pos_min[t^1]);
    	}return ret == pos_min[0] ? -1 : ret;
    }
    inline ll query_max(ll s,ll t){
    	if(s > t) return -1;
    	ll ret = pos_max[0];
    	for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1){
    		if(~s&1) ret = max(ret,pos_max[s^1]);
    		if( t&1) ret = max(ret,pos_max[t^1]);
    	}return ret == pos_max[0] ? -1 : ret;
    }
    int main(){
    	read(n);for(M=1;M<(n+1);M<<=1);
    	memset(pos_min, 0x3f,sizeof pos_min);
    	memset(pos_max,-0x3f,sizeof pos_max);
    	for(ll i=0;i<n;++i){
    		read(a[n-i-1]);b[i] = a[n-i-1];
    	}
    	sort(b,b+n);
    	for(ll i=0;i<n;++i){
    		a[i] = lower_bound(b,b+n,a[i]) - b + 1;
    	}a[n] = 0;
    	da(a,n+1,n+1);get_h(a,n);pre();
    	ll ans = 0;
    	for(ll i=n-1;i>=0;--i){
    		ans += n-i;
    		ll x = query_max(1,rank[i]-1);
    		ll y = query_min(rank[i]+1,n);
    		if(x != -1 && y != -1) ans -= max(lcp(rank[i],x),lcp(rank[i],y));
    		else if(x == -1 && y != -1) ans -= lcp(rank[i],y);
    		else if(x != -1 && y == -1) ans -= lcp(rank[i],x);
    		printf("%lld
    ",ans);
    		modify(rank[i],rank[i]);
    	}
    	getchar();getchar();
    	return 0;
    }
    
  • 相关阅读:
    C#输出JS代码封装类Alart
    我的汇编学习之路(2)win8(64位)下安装debug
    .NET使用一般处理程序生成验证码
    ?运算符(null合并运算符)和三木运算符
    讲解:小菜鸟自制的.NET实体类生成器
    我的汇编学习之路(1)进制转换
    未来
    callee,caller区别
    string::size_type
    ubuntu 12.04 LTS u盘安装
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6410533.html
Copyright © 2011-2022 走看看