zoukankan      html  css  js  c++  java
  • 5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集

    LINK:波波老师

    LINK:同bzoj 1396 识别子串

    不过前者要求线性做法 后者可以log过。实际上前者也被我一个log给水过了.

    其实不算很水 我自认跑的很快罢了.

    都是求经过一个位置的最短的 在整个字符串中只出现过一次的子串。

    SAM很容易完成这个东西.

    考虑对于计算每个节点的贡献 容易发现是一个区间整体赋值和一个等差数列 不过太懒了不想维护这个等差数列 我反着建SAM维护最右左端点了。

    就变成了两个区间最值问题。完全可以标记永久化 可能有点卡空间。

    当然考场上也思考了O(n)的做法 先考虑等差数列那个地方 由于right集大小为1 那么显然这个点的len是一直到之后都有效所以可以直接标记最后取max

    考虑区间取min操作。当时想法是排序后利用并查集 排列可以利用桶排 然后复杂度就降到(ncdor (a))

    可以发现桶排序之后可以利用单调队列+链表 来实现O(n).细节有点繁琐 不再赘述.

    标记永久化还是很快滴 (比其他dalao写zkw线段树要快一点.当时我也想写zkw线段树 不过不太熟练所以就没用

    const int MAXN=5000010;
    int n,cnt=1,last=1;
    int sum[MAXN<<2],w[MAXN<<2];//一个取max 一个取min.
    struct wy
    {
    	int ch[5];
    	int fa,len;
    }t[MAXN<<1];
    char a[MAXN];ll ans;
    int c[MAXN],vis[MAXN<<1],s[MAXN<<1];
    inline void insert(int x)
    {
    	int p=last;
    	int np=last=++cnt;
    	len(np)=len(p)+1;
    	while(p&&!t[p].ch[x])
    	{
    		t[p].ch[x]=np;
    		p=f(p);
    	}
    	if(!p)f(np)=1;
    	else
    	{
    		int q=t[p].ch[x];
    		if(len(q)==len(p)+1)f(np)=q;
    		else
    		{
    			int nq=++cnt;
    			t[nq]=t[q];
    			len(nq)=len(p)+1;
    			f(np)=f(q)=nq;
    			while(p&&t[p].ch[x]==q)
    			{
    				t[p].ch[x]=nq;
    				p=f(p);
    			}
    		}
    	}
    }
    inline void topsort()
    {
    	rep(1,cnt,i)++c[len(i)];
    	rep(1,n,i)c[i]+=c[i-1];
    	rep(1,cnt,i)s[c[len(i)]--]=i;
    }
    inline void change(int p,int l,int r,int L,int R,int x)
    {
    	if(l==L&&r==R)return sum[p]=min(sum[p],x),void();
    	int mid=(l+r)>>1;
    	if(L>mid)return change(yy,mid+1,r,L,R,x),void();
    	if(R<=mid)return change(zz,l,mid,L,R,x),void();
    	change(zz,l,mid,L,mid,x);change(yy,mid+1,r,mid+1,R,x);
    }
    inline void modify(int p,int l,int r,int L,int R,int x)
    {
    	if(l==L&&r==R)return w[p]=max(w[p],x),void();
    	int mid=(l+r)>>1;
    	if(L>mid)return modify(yy,mid+1,r,L,R,x),void();
    	if(R<=mid)return modify(zz,l,mid,L,R,x),void();
    	modify(zz,l,mid,L,mid,x);modify(yy,mid+1,r,mid+1,R,x);
    }
    inline void dfs(int p,int l,int r,int S,int W)
    {
    	S=min(S,sum[p]);W=max(W,w[p]);
    	if(l==r)
    	{
    		if(W==0)W=-INF;
    		ans+=min(S,l-W+1);
    		return;
    	}
    	int mid=(l+r)>>1;
    	dfs(zz,l,mid,S,W);
    	dfs(yy,mid+1,r,S,W);
    }
    int main()
    {
    	freopen("bobo.in","r",stdin);
        freopen("bobo.out","w",stdout);
    	gc(a);n=strlen(a+1);
    	fep(n,1,i)insert(a[i]-'a'),vis[last]=i;
    	topsort();
    	memset(sum,0x3f,sizeof(sum));
    	fep(cnt,2,i)
    	{
    		int x=s[i];
    		//cout<<vis[x]<<' '<<len(x)<<endl;
    		if(vis[x]==-1)vis[f(x)]=-1;
    		else 
    		{
    			if(!vis[f(x)])vis[f(x)]=vis[x];
    			else vis[f(x)]=-1;
    		}
    		if(vis[x]!=-1)
    		{
    			change(1,1,n,vis[x],vis[x]+len(f(x)),len(f(x))+1);
    			modify(1,1,n,vis[x]+len(f(x)),vis[x]+len(x)-1,vis[x]);
    		}
    	}
    	dfs(1,1,n,INF,0);putl(ans);
    	return 0;
    }
    
  • 相关阅读:
    正则判断手机号是不是11位
    jQuery, js 验证两次输了密码的一相同
    数据库读取图片
    使用 jQuery 修改 css 中带有 !important 的样式属性
    PHP去掉最后一个字符
    按钮
    js 实现两种99乘法表
    PHP 各种循环
    thinkphp的空控制器和空操作以及对应解决方法
    ThinkPHP框架知识
  • 原文地址:https://www.cnblogs.com/chdy/p/13018828.html
Copyright © 2011-2022 走看看