2017.3.27 生成魔咒 思考记录(有史以来最不容易)

         做了三天,实在太坑了   

      

        终于100文章了

        

 这个题要求每次在单词的末尾加一个字母,再查询本质不同的串的个数、、

        既然是子串满足连续性,那就可以后缀数组+lcp搞了;

           具体计算方式:

         由于后缀数组的开头是包括所有子串开头的,所以便利sa即可出解。

                具体要怎么做?

             先放宽条件:如果只有一次查询,对字符串建好后缀数组后,由于sa相临的后缀的前缀一定是最相似的,所以重复的情况一定出现在这两个串中,对于这两个串的lcp,一定是会被算两次的   :  如12 2121  和12 1 第一个可拆成1、 12、 122、 1221、 12212、 122121    ;;第二个串可拆成1、 12、 121    可以发现lcp”1、 12“被算了两次,所以在计算第二个串时要减去和第一个串的lcp;;

              看直观的表 :
         


但是它是每一次加就有一次询问,而且加的时候对每个后缀都有影响,可能会导致lcp的改变,,那么怎么办呢?

其实可以把串反转,,因为根据每个串的末端也可以找出所有的子串;;

这样一次就只用加一个字符串了;

但是,这似乎就需要动态维护sa了、、、、、

询问xyx:   这不是可以log n 找位置吗;;

询问shallwe:  我没空啊,你去问cjc、

询问cjc:    xym研究过后缀数组动态加后缀,你可以去问他;

询问xym:    用splay离线操作加点啊,shallwe 跑的比我的快,你可以问他、、

、、、、、、、、、、、、、、、、、、、、、、、

然后就只能自己看题解了,因为splay维护sa太玄学

然而题解更玄学、



所以打了个splay

千万不要用 log 函数!!!!!
!!




!!!





!!!!!


而且lcp时要想清楚

#include<iostream>
#include<cstdio>
using namespace std;
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#define N 300009
#define inf 1e9+10
#define ll long long
map<ll,ll>ma;
ll max1,min1;
struct tree
{
	ll zhi;
	tree *ch[2],*fu; 
	ll getwh()
	{
	 return 	fu->ch[0]==this?0:1;	
	}
    void lian(ll wh,tree *child);
 }p[200001],*null,*root; 
    ll tot;
void tree::lian(ll wh,tree *child){
ch[wh]=child;
if(child!=null)
child->fu=this;
	}	
tree *jian(ll k)
    {
    	tree *now=p+ ++tot;   	
    	now->zhi=k;
    	now->ch[1]=now->ch[0]=now->fu=null;
    return now;	    	
	}
  void rotate(tree *&k)
  {
  	tree *fu=k->fu;
  	tree *ye=k->fu->fu;
   ll wh=k->getwh();
    fu->lian(wh,k->ch[wh^1]);
    k->lian(wh^1,fu);
    k->fu=ye;
    if(ye!=null)
    ye->ch[ye->ch[0]==fu?0:1]=k;
  }
  void splay(tree *now,tree *tar)
  {
  	for(;now->fu!=tar;rotate(now))
  	  if(now->fu->fu!=tar)now->fu->getwh()==now->getwh()?rotate(now->fu):rotate(now);
  	  if(tar==null)
  root=now;  
  }
  void ins(ll k)
  {
	  tree *now=root,*last=null,*newone=jian(k);
  	while(now!=null)
  	{last=now;
  	if(k>now->zhi)
	  min1=max(min1,now->zhi),now=now->ch[1];
	  else if(k<now->zhi)max1=min(max1,now->zhi),now=now->ch[0];
	}
  	if(last==null)
  	{
  		root=newone;
  	    return;		 		
	}
  	if(last->zhi>k)
  	{
  	    last->lian(0,newone);	
	}else
	{
		last->lian(1,newone);
	}
  }
ll n,s1[N],s2[N],cc[N],sa[N],height[N],rank[N],i,j,k,s[N],f[N][21];
ll ans;
inline bool cmp(ll *y, ll a, ll b, ll k) {
    ll arank1 = y[a];
    ll brank1 = y[b];
    ll arank2 = a + k >= n ? -1 : y[a + k];
    ll brank2 = b + k >= n ? -1 : y[b + k];
    return arank1 == brank1 && arank2 == brank2;
}
inline void da() {
    ll *x = s1, *y = s2;
    ll m = 100005;
	for (ll i = 0; i < m; i++) cc[i] = 0;
    for (ll i = 0; i < n; i++) ++cc[x[i] = s[i]];
    for (ll i = 1; i < m; i++) cc[i] += cc[i - 1];
    for (ll i = n - 1; i >= 0; i--) sa[--cc[x[i]]] = i;
    for (ll k = 1; k <= n; k <<= 1) {
        ll p = 0;
        for (ll i = n - k; i < n; i++) y[p++] = i;
        for (ll i = 0; i < n; i++)
            if (sa[i] >= k) y[p++] = sa[i] - k;
		for (ll i = 0; i < m; i++) cc[i] = 0;
        for (ll i = 0; i < n; i++) ++cc[x[y[i]]];
        for (ll i = 1; i < m; i++) cc[i] += cc[i - 1];
        for (ll i = n - 1; i >= 0; i--) sa[--cc[x[y[i]]]] = y[i];
		swap(x, y);
        m = 1; x[sa[0]] = 0;
        for (ll i = 1; i < n; i++)
            x[sa[i]] = cmp(y, sa[i], sa[i - 1], k) ? m - 1 : m++;
		
		if (m >= n) break;
    }
}
inline void calcheight() {
    for (ll i = 0; i < n; i++) rank[sa[i]] = i;
    height[0] = 0;
    ll k = 0;
    for (ll i = 0; i < n; i++) {
        if (!rank[i]) continue;
        ll j = sa[rank[i] - 1];
        if (k) k--;
        while (s[i + k] == s[j + k]) k++;
        height[rank[i]] =f[rank[i]][0]=k;
    }
}
void rmq()
{
	for(i=1;i<=20;i++)
	{
		for(j=0;j+(1<<i)-1<=n;j++)
		{
			f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);		
		}	
	}
}
ll woc[100001],lg2[200001];
int main()
{
	null=p;
null->zhi=0;
null->fu=null->ch[0]=null->ch[1]=null;
root=null;
	lg2[1]=0;lg2[2]=1;lg2[3]=1;
	for(i=2;i<=16;i++)
	{
		int beg=1<<i;
		int end=1<<(i+1);
		for(j=beg;j<end;j++)lg2[j]=i;	
	}
	scanf("%lld",&n);
	for(i=n-1;i>=0;i--)		scanf("%lld",&s[i]),woc[i]=s[i];
	sort(woc,woc+n);
	ll cnt=-1;
	for(i=0;i<n;i++)
	{
		if(!ma[woc[i]])ma[woc[i]]=++cnt;
	}
	for(i=0;i<n;i++)s[i]=ma[s[i]];
 da();calcheight();
 rmq();
 for(i=n-1;i>=0;i--)
 {
 	max1=inf;
 	min1=-1;
 	ll k=rank[i],biao,lin1;
 	ins(k);
 	if(max1==inf&&min1==-1)
 	{
 	ans+=n-i;	
printf("%lld\n",ans);
	}
 	else
 	if(max1==inf&&min1!=-1)
 	{
 		 min1++;
	 biao=(lg2[k-min1+1]);
	 lin1=min(f[min1][biao],f[k-(1<<biao)+1][biao]); 
	 ans+=n-i-lin1;
printf("%lld\n",ans);
	}else
 	if(max1!=inf&&min1==-1)
 	{
 	k++;
	 biao=lg2[max1-k+1];
	 lin1=min(f[k][biao],f[max1-(1<<biao)+1][biao]);
	 ans+=n-i-lin1;
 	printf("%lld\n",ans);
	 }
 	else
 	{
 	min1++;
 	 biao=lg2[max1-min1+1];
 	 lin1=min(f[min1][biao],f[max1-(1<<biao)+1][biao]);
	 ans-= (n-sa[max1])-lin1;
	 biao=lg2[k-min1+1];
	 lin1=min(f[min1][biao],f[k-(1<<biao)+1][biao]); 
	 ans+=(n-i)-lin1;
	 k++;
	 biao=lg2[max1-k+1];
	 lin1=min(f[k][biao],f[max1-(1<<biao)+1][biao]);
	 ans+=(n-sa[max1])-lin1;
	 printf("%lld\n",ans);
 	}
 }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值