zoukankan      html  css  js  c++  java
  • BZOJ.4516.[SDOI2016]生成魔咒(后缀数组 RMQ)

    题目链接

    后缀自动机做法见这(超好写啊)

    后缀数组是可以做的: 本质不同的字符串的个数为 (子串个数-sum_{ht[i]}),即 (frac{n(n+1)}{2}-sum_{ht[i]}).
    如果是每次往后边插入字符,会改变SA[]。但如果向前边插入字符,相当于只加入了一个后缀。
    所以离线,把原串反过来。
    每次插入一个字符,即新增一个前缀i,它的贡献是(len-max(lcp(pre,i),lcp(i,nxt))),其中(pre,nxt)为与后缀(i)(当前)排名相邻的两个串。
    用set维护当前的排名,RMQ求lcp。(懒得写(O(n))的维护单调栈正反跑两遍)

    做了几道后缀数组了,竟然真没写过lcp(也许是太简单了)。

    (10^5)很容易爆int!

    //13228kb	1000ms
    #include <set>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define gc() getchar()
    const int N=1e5+5,B=17;
    
    int n,cnt,A[N],ref[N],tm[N],sa[N],sa2[N],rk[N],ht[N],log2[N],st[N][18];
    std::set<int> set_rk;
    std::set<int>::iterator it;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    int Find(int x)
    {
    	int l=1,r=cnt,mid;
    	while(l<r)
    		if(ref[mid=l+r>>1]<x) l=mid+1;
    		else r=mid;
    	return l;
    }
    void Get_SA()
    {
    	int *x=rk,*y=sa2,m=n+1;
    	for(int i=0; i<=m; ++i) tm[i]=0;
    	for(int i=1; i<=n; ++i) ++tm[x[i]=A[i]];
    	for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
    	for(int i=n; i; --i) sa[tm[x[i]]--]=i;
    	for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
    	{
    		for(int i=n-k+1; i<=n; ++i) y[++p]=i;
    		for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;
    
    		for(int i=0; i<=m; ++i) tm[i]=0;
    		for(int i=1; i<=n; ++i) ++tm[x[i]];
    		for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
    		for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];
    
    		std::swap(x,y), x[sa[1]]=p=1;
    		for(int i=2; i<=n; ++i)
    			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
    		if(p>=n) break;
    	}
    	for(int i=1; i<=n; ++i) rk[sa[i]]=i;
    	ht[1]=0;
    	for(int k=0,p,i=1; i<=n; ++i)
    	{
    		if(rk[i]==1) continue;
    		if(k) --k;
    		p=sa[rk[i]-1];
    		while(i+k<=n && p+k<=n && A[i+k]==A[p+k]) ++k;
    		ht[rk[i]]=k;
    	}
    }
    void Init_RMQ()
    {
    	for(int i=1; i<=n; ++i) st[i][0]=ht[i];
    	log2[1]=0;
    	for(int i=2; i<=n; ++i) log2[i]=log2[i>>1]+1;
    	for(int j=1; j<=log2[n]; ++j)
    		for(int i=n-(1<<j-1); i; --i)
    			st[i][j]=std::min(st[i][j-1],st[i+(1<<j-1)][j-1]);
    }
    int LCP(int l,int r)
    {
    	if(l>r) std::swap(l,r);
    	++l; int k=log2[r-l+1];
    	return std::min(st[l][k],st[r-(1<<k)+1][k]);
    }
    
    int main()
    {
    	n=read();
    	for(int i=1; i<=n; ++i) ref[i]=A[i]=read();
    	std::sort(ref+1,ref+1+n), cnt=1;
    	for(int i=2; i<=n; ++i) if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
    	for(int i=1; i<=n; ++i) A[i]=Find(A[i]);
    //	for(int i=1,lim=n>>1; i<=lim; ++i) std::swap(A[i],A[n-i+1]);//惊了 还写错了== 还是写reverse吧  
    	std::reverse(A+1,A+1+n);
    	Get_SA(), Init_RMQ();
    	long long res=0;
    	set_rk.insert(0);
    	for(int tmp,i=n; i; --i)//从头开始加入字符(前缀)。
    	{
    		tmp=0, it=set_rk.upper_bound(rk[i]);
    		if(it!=set_rk.end()) tmp=LCP(rk[i],*it);
    		if((--it)!=set_rk.begin()) tmp=std::max(tmp,LCP(*it,rk[i]));
    		res+=n-i+1-tmp, set_rk.insert(rk[i]);
    		printf("%lld
    ",res);
    	}
    
    	return 0;
    }
    

    Hash暴力:(60)
    那个暴力后缀数组理论也有60啊但是只有20。。懒得看了

    #include <set>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define gc() getchar()
    #define Enter putchar('
    ')
    typedef unsigned long long ull;
    const ull seed=131;
    const int N=2007;
    
    int n,cnt,A[100005],ref[100005],tm[N],sa[N],sa2[N],rk[N],ht[N];
    ull pw[N];
    std::set<ull> st;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    int Find(int x)
    {
    	int l=1,r=cnt,mid;
    	while(l<r)
    		if(ref[mid=l+r>>1]<x) l=mid+1;
    		else r=mid;
    	return l;
    }
    void Get_SA(int n)
    {
    	int *x=rk,*y=sa2,m=n;//std::min(cnt,n)+2;
    	for(int i=0; i<=m; ++i) tm[i]=0;
    	for(int i=1; i<=n; ++i) ++tm[x[i]=A[i]];
    	for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
    	for(int i=n; i; --i) sa[tm[x[i]]--]=i;
    	for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
    	{
    		for(int i=n-k+1; i<=n; ++i) y[++p]=i;
    		for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;
    
    		for(int i=0; i<=m; ++i) tm[i]=0;
    		for(int i=1; i<=n; ++i) ++tm[x[i]];
    		for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
    		for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];
    
    		std::swap(x,y), x[sa[1]]=p=1;
    		for(int i=2; i<=n; ++i)
    			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
    		if(p>=n) break;
    	}
    	for(int i=1; i<=n; ++i) rk[sa[i]]=i;
    	ht[1]=0;
    	for(int k=0,p,i=1; i<=n; ++i)
    	{
    		if(rk[i]==1) continue;
    		if(k) --k;
    		p=sa[rk[i]-1];
    		while(i+k<=n && p+k<=n && A[i+k]==A[p+k]) ++k;
    		ht[rk[i]]=k;
    	}
    //	puts("Output:");
    //	for(int i=1; i<=n; ++i) printf("SA:No:%d pos:%d
    ",i,sa[i]);Enter;
    //	for(int i=1; i<=n; ++i) printf("RK:%d rank:%d
    ",i,rk[i]);Enter;
    //	for(int i=1; i<=n; ++i) printf("HT:%d ht:%d
    ",i,ht[i]);Enter;
    
    	int res=n-sa[1]+1;
    	for(int i=2; i<=n; ++i) res+=n-sa[i]+1-ht[i];
    	printf("%d
    ",res);
    }
    void N_2(){
    	for(int i=1; i<=n; ++i) Get_SA(i);
    }
    void HASH(){
    	pw[0]=1;
    	for(ull i=1; i<=n; ++i) pw[i]=pw[i-1]*i;
    	int res=0;
    	for(int p=1; p<=n; ++p)
    	{
    		ull hs=0;
    		for(int i=p; i; --i)
    		{
    			hs=hs*seed+A[i];
    			if(!st.count(hs)) ++res,st.insert(hs);
    		}
    		printf("%d
    ",res);
    	}
    }
    
    int main()
    {
    	freopen("incantation.in","r",stdin);
    	freopen("incantation.out","w",stdout);
    
    	n=read();
    	for(int i=1; i<=n; ++i) ref[i]=A[i]=read();
    	std::sort(ref+1,ref+1+n), cnt=1;
    	for(int i=2; i<=n; ++i) if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
    	for(int i=1; i<=n; ++i) A[i]=Find(A[i]);
    	if(n<=2000) {HASH(); return 0;}
    	if(n<=2000) {N_2(); return 0;}//mmp
    	for(int i=1; i<=n; ++i) printf("%d
    ",i);
    	
    
    	fclose(stdin);fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    上传图片,正在加载,loading
    bootstrap-table(2)问题集
    Bootstarp-table入门(1)
    bootstrap-table给每一行数据添加按钮,并绑定事件
    获得 bootstrapTable行号index
    Http请求中Content-Type讲解以及在Spring MVC注解中produce和consumes配置详解
    enums应用详解
    bootstrap-table.min.js不同版本返回分页参数不同的问题
    Linux学习笔记之Linux目录结构、磁盘命名、启动过程
    Linux学习笔记之Linux相关知识
  • 原文地址:https://www.cnblogs.com/SovietPower/p/8709026.html
Copyright © 2011-2022 走看看