zoukankan      html  css  js  c++  java
  • 【刷题】BZOJ 4199 [Noi2015]品酒大会

    Description

    一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。在大会的晚餐上,调酒师Rainbow调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 i 杯酒 (1≤i≤n) 被贴上了一个标签 s_i ,每个标签都是 26 个小写英文字母之一。设 Str(l,r) 表示第 l 杯酒到第 r 杯酒的 r-l+1 个标签顺次连接构成的字符串。若 Str(p,po)=Str(q,qo) ,其中 1≤p≤po≤n,1≤q≤qo≤n,p≠q,po-p+1=qo-q+1=r ,则称第 p 杯酒与第 q 杯酒是“ r 相似”的。当然两杯“ r 相似”(r>1)的酒同时也是“ 1 相似”、“ 2 相似”、……、“ (r-1) 相似”的。在品尝环节上,品酒师Freda轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 i 杯酒 (1≤i≤n) 的美味度为 a_i 。现在Rainbow公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 a_p a_q 的酒。现在请各位品酒师分别对于 r=0,1,2,?,n-1 ,统计出有多少种方法可以选出 2 杯“ r 相似”的酒,并回答选择 2 杯“ r 相似”的酒调兑可以得到的美味度的最大值。

    Input

    输入文件的第1行包含1个正整数 n ,表示鸡尾酒的杯数。

    第 2 行包含一个长度为 n 的字符串 S ,其中第 i 个字符表示第 i 杯酒的标签。

    第 3 行包含 n 个整数,相邻整数之间用单个空格隔开,其中第 i 个整数表示第 i 杯酒的美味度 a_i 。

    n=300,000 |a_i |≤1,000,000,000

    Output

    输出文件包括 n 行。第 i 行输出 2 个整数,中间用单个空格隔开。

    第 1 个整数表示选出两杯“ (i-1)" " 相似”的酒的方案数,

    第 2 个整数表示选出两杯“ (i-1) 相似”的酒调兑可以得到的最大美味度。

    若不存在两杯“ (i-1) 相似”的酒,这两个数均为 0 。

    Sample Input

    10
    ponoiiipoi
    2 1 4 7 4 8 3 6 4 7

    Sample Output

    45 56
    10 56
    3 32
    0 0
    0 0
    0 0
    0 0
    0 0
    0 0
    0 0
    【样例说明1】
    用二元组 (p,q) 表示第 p 杯酒与第 q 杯酒。
    0 相似:所有 45 对二元组都是 0 相似的,美味度最大的是 8×7=56 。
    1 相似: (1,8) (2,4) (2,9) (4,9) (5,6) (5,7) (5,10) (6,7) (6,10) (7,10) ,最大的 8×7=56 。
    2 相似: (1,8) (4,9) (5,6) ,最大的 4×8=32 。
    没有 3,4,5,?,9 相似的两杯酒,故均输出 0 。

    Solution

    对于两个位置,它们的最大相似度就是以它们开头的后缀的LCP长度
    那么把SA和height求出来,并对于每一个height求一段区间,使得这段区间中当前height值最小,以致在这段区间中,当前位置的左边选一个位置,当前位置的右边选一个位置,这两个位置的相似度必定取决于当前的height
    于是就可以累加答案,过程中注意对于height值相等的怎么处理,把排在前面的视为更大或更小都可以,但是不能视为完全相同而置于同一段,会算重
    那么第二问,既然在算第一问的时候方案数的累计是左边一段的一个位置加上右边一段的一个位置,那么就只要在左边一段选个最大的,乘上右边一段的最大的就好了,当然,负数的处理就是左边最小的乘上右边最小的,两个值取更大。
    这个最大最小值用ST表预处理

    #include<bits/stdc++.h>
    #define ui unsigned int
    #define ll long long
    #define db double
    #define ld long double
    #define ull unsigned long long
    const int MAXN=300000+10;
    const ll inf=0x3f3f3f3f3f3f3f3fll;
    int n,m,SA[MAXN],height[MAXN],nxt[MAXN],rk[MAXN],cnt[MAXN],a[MAXN],lp[MAXN],rp[MAXN],Mx[21][MAXN],Mn[21][MAXN],lg[MAXN];
    ll ans1[MAXN],ans2[MAXN];
    char s[MAXN];
    template<typename T> inline void read(T &x)
    {
    	T data=0,w=1;
    	char ch=0;
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    	x=data*w;
    }
    template<typename T> inline void write(T x,char ch='')
    {
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    	if(ch!='')putchar(ch);
    }
    template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
    template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
    template<typename T> inline T min(T x,T y){return x<y?x:y;}
    template<typename T> inline T max(T x,T y){return x>y?x:y;}
    inline void GetSA()
    {
    	m=300;
    	for(register int i=1;i<=n;++i)rk[i]=s[i],cnt[rk[i]]++;
    	for(register int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
    	for(register int i=n;i>=1;--i)SA[cnt[rk[i]]--]=i;
    	for(register int k=1,ps;k<=n;k<<=1)
    	{
    		ps=0;
    		for(register int i=n-k+1;i<=n;++i)nxt[++ps]=i;
    		for(register int i=1;i<=n;++i)
    			if(SA[i]>k)nxt[++ps]=SA[i]-k;
    		for(register int i=1;i<=m;++i)cnt[i]=0;
    		for(register int i=1;i<=n;++i)cnt[rk[i]]++;
    		for(register int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
    		for(register int i=n;i>=1;--i)SA[cnt[rk[nxt[i]]]--]=nxt[i];
    		std::swap(rk,nxt);
    		rk[SA[1]]=1,ps=1;
    		for(register int i=2;i<=n;rk[SA[i]]=ps,++i)
    			if(nxt[SA[i]]!=nxt[SA[i-1]]||nxt[SA[i]+k]!=nxt[SA[i-1]+k])ps++;
    		if(ps>=n)break;
    		m=ps;
    	}
    	for(register int i=1,j,k=0;i<=n;height[rk[i++]]=k)
    		for(k=k?k-1:k,j=SA[rk[i]-1];s[i+k]==s[j+k];++k);
    }
    inline void init()
    {
    	for(register int j=1;j<20;++j)
    		for(register int i=1;i+(1<<j-1)<=n;++i)
    		{
    			Mx[j][i]=max(Mx[j-1][i],Mx[j-1][i+(1<<j-1)]);
    			Mn[j][i]=min(Mn[j-1][i],Mn[j-1][i+(1<<j-1)]);
    		}
    }
    inline int query(int tp,int l,int r)
    {
    	int k=lg[r-l+1];
    	if(!tp)return max(Mx[k][l],Mx[k][r-(1<<k)+1]);
    	else return min(Mn[k][l],Mn[k][r-(1<<k)+1]);
    }
    int main()
    {
    	read(n);scanf("%s",s+1);
    	GetSA();
    	ans2[0]=-inf;
    	for(register int i=1;i<=n;++i)
    	{
    		read(a[i]),ans2[i]=-inf;
    		lg[i]=log(i)/log(2),Mx[0][rk[i]]=Mn[0][rk[i]]=a[i];
    	}
    	init();
    	for(register int i=2,j;i<=n;lp[i]=j,++i)
    		for(j=i-1;j>=2&&height[j]>=height[i];j=lp[j]);
    	for(register int i=n,j;i>=2;rp[i]=j,--i)
    		for(j=i+1;j<=n&&height[j]>height[i];j=rp[j]);
    	for(register int i=2;i<=n;++i)
    	{
    		++lp[i],--rp[i];
    		ans1[height[i]]+=1ll*(i-lp[i]+1)*(rp[i]-i+1);
    		chkmax(ans2[height[i]],1ll*query(0,lp[i]-1,i-1)*query(0,i,rp[i]));
    		chkmax(ans2[height[i]],1ll*query(1,lp[i]-1,i-1)*query(1,i,rp[i]));
    	}
    	for(register int i=n-1;i>=0;--i)ans1[i]+=ans1[i+1],chkmax(ans2[i],ans2[i+1]);
    	for(register int i=0;i<n;++i)write(ans1[i],' '),write(ans2[i]!=-inf?ans2[i]:0,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    48-最长不含重复字符的子字符串
    51-数组中的逆序对
    字符串的排列
    二叉树转链表
    求根
    构造二叉树
    二叉树中序遍历
    反转链表系列
    斐波那契系列
    f.lux
  • 原文地址:https://www.cnblogs.com/hongyj/p/9210979.html
Copyright © 2011-2022 走看看