zoukankan      html  css  js  c++  java
  • 【[NOI2015]品酒大会】

    可能是最傻的做法了

    暴力单调栈+(st)

    首先看到这道题就基本知道这是个(SA)了,先无脑敲上(SA)和求(height)的板子

    之后尝试搞一下第一问

    发现第一问就是求出满足(lcp(i,j)>=k)((i,j))有多少对

    我们可以用一个暴力合并的单调栈来做

    现在的问题转化为求出(height)数组所有子区间的最小值的和

    我们可以考虑一个动态往序列末尾加数的过程

    也就是我们往末尾加一个数都会和之前所有的数形成一个新的区间

    考虑快速算出这些区间的最小值的和

    我们可以对每一个数存储一个(a_i),表示(i)到当前序列末尾的最小值是多少

    我们每次加入一个数可以对更新一下所有的(a_i),把所有比当前加入的数大的(a_i)变成当前数就好了

    这不就(T)了吗

    我们发现我们只需要求出所有(a_i)的和,并不需要关心这个(i)来自哪里,于是我们可以把相等的(a_i)放在一起计算,也就是每次新加入一个数就暴力扫一遍把那些比当前加入数大的合并到一个(a_i)

    看起来复杂度并不科学,但是最坏情况下就相当于是一个线段树的复杂度了,(O(n))的,跑的还挺快的

    同时还需要维护一个时间戳,在被合并的时候利用时间戳统计一下这个元素的贡献

    之后第二问,我们可以直接强上单调栈

    左右两边都先用单调栈扫一遍,扫出每个点往左往右能最远达到哪里,之后我们用一个(st)表维护区间的最大值和最小值,每次只需要从这个点左边的区间和右边的区间里选出最大值和最小值来组合就行了

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define re register
    #define LL long long
    #define maxn 300005
    inline LL read()
    {
    	re char c=getchar();re LL x=0,r=1;
    	while(c<'0'||c>'9') {if(c=='-') r=-1;c=getchar();}
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return r*x;
    }
    int n,m,top;
    int a[maxn],cnt[maxn],T[maxn],L[maxn],R[maxn];
    char S[maxn];
    int tp[maxn],tax[maxn],sa[maxn],rk[maxn],het[maxn],log_2[maxn],st[maxn];
    LL pre[maxn],Ans[maxn];
    LL St[2][maxn][20];
    inline void qsort()
    {
    	for(re int i=0;i<=m;i++) tax[i]=0;
    	for(re int i=1;i<=n;i++) tax[rk[i]]++;
    	for(re int i=1;i<=m;i++) tax[i]+=tax[i-1];
    	for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
    } 
    inline LL max(LL a,LL b){return (a>b)?a:b;}
    inline LL min(LL a,LL b){return (a<b)?a:b;}
    inline LL ask(int o,int l,int r)
    {
    	int k=log_2[r-l+1];
    	if(o) return max(St[1][l][k],St[1][r-(1<<k)+1][k]);
    		else return min(St[0][l][k],St[0][r-(1<<k)+1][k]);
    }
    int main()
    {
    	n=read();scanf("%s",S+1);
    	memset(St[0],0x3f3f3f3f,sizeof(St[0]));
    	memset(St[1],-0x3f3f3f3f,sizeof(St[1]));
    	memset(Ans,-0x3f3f3f3f,sizeof(Ans));
    	m=750;
    	for(re int i=1;i<=n;i++) rk[i]=S[i],tp[i]=i;
    	qsort();
    	for(re int w=1,p=0;p<n;m=p,w<<=1)
    	{
    		p=0;
    		for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
    		for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
    		qsort();
    		for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
    		rk[sa[1]]=p=1;
    		for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    	}
    	int k=0;
    	for(re int i=1;i<=n;i++)
    	{
    		if(k) --k;
    		int j=sa[rk[i]-1];
    		while(S[i+k]==S[j+k]) ++k;
    		het[rk[i]]=k;
    	}
    	for(re int i=2;i<=n;i++)
    	{
    		int now=1;
    		while(top&&a[top]>=het[i])
    			now+=cnt[top],pre[a[top]]+=(LL)((LL)i-(LL)T[top])*(LL)cnt[top],top--;
    		a[++top]=het[i],T[top]=i,cnt[top]=now;
    	}
    	while(top) pre[a[top]]+=(LL)((LL)n+1ll-(LL)T[top])*(LL)cnt[top],top--;
    	for(re int i=n-1;i>=0;--i) pre[i]+=pre[i+1];
    	
    	for(re int i=1;i<=n;i++)
        {
            while(top&&het[i]<het[st[top]]) R[st[top]]=i,top--;
            st[++top]=i;
        }
        while(top) R[st[top]]=n+1,top--;
        for(re int i=n;i;--i)
        {
            while(top&&het[i]<het[st[top]]) L[st[top]]=i,top--;
            st[++top]=i;
        }
        while(top) L[st[top]]=1,top--;
        
        for(re int i=1;i<=n;i++) St[0][rk[i]][0]=St[1][rk[i]][0]=read();
    	for(re int i=2;i<=n;i++) log_2[i]=1+log_2[i>>1];
    	for(re int j=1;j<=19;j++)
    		for(re int i=1;i+(1<<j)-1<=n;i++)
    			St[0][i][j]=min(St[0][i][j-1],St[0][i+(1<<(j-1))][j-1]),
    			St[1][i][j]=max(St[1][i][j-1],St[1][i+(1<<(j-1))][j-1]);
    			
        for(re int i=1;i<=n;i++)
    	{
        	int l=L[i],r=R[i]-1;
        	if(l>i-1) continue;
    		if(i>r) continue; 
    		LL A=ask(1,l,i-1),B=ask(0,l,i-1),C=ask(1,i,r),D=ask(0,i,r);
        	if(Ans[het[i]]<A*C) Ans[het[i]]=A*C; if(Ans[het[i]]<B*D) Ans[het[i]]=B*D;
        	if(Ans[het[i]]<A*D) Ans[het[i]]=A*D; if(Ans[het[i]]<B*C) Ans[het[i]]=B*C;
    	}
    	for(re int i=n-1;i>=0;--i) Ans[i]=max(Ans[i],Ans[i+1]);
    	for(re int i=0;i<n;i++)
    	if(pre[i]) printf("%lld %lld
    ",pre[i],Ans[i]);
    		else printf("%lld ",pre[i]),putchar(48),putchar(10);
    	return 0;
    }
    
  • 相关阅读:
    【收藏】如何理解二维数组
    【algo&ds】9.拓扑排序、AOV&AOE、关键路径问题
    【algo&ds】8.最小生成树
    【algo&ds】7.最短路径问题
    【algo&ds】6.图及其存储结构、遍历
    【c&c++】变量初始化
    【algo&ds】【pat】5.并查集及其应用
    【algo&ds】4.B树、字典树、红黑树、跳表
    【algo&ds】4.树和二叉树、完全二叉树、满二叉树、二叉查找树、平衡二叉树、堆、哈夫曼树、散列表
    【algo&ds】3.栈和队列
  • 原文地址:https://www.cnblogs.com/asuldb/p/10205622.html
Copyright © 2011-2022 走看看