bzoj 4199: [Noi2015]品酒大会【后缀数组+单调栈+并查集】

用SA求出height数组,然后发现每个height值都有一个贡献区间(因为点对之间要依次取min)

用单调栈处理出区间,第一问就做完了

然后用并查集维护每个点的贡献(?),从大到小枚举height,因为这样区间是不断增大的所以并查集合并即可

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N=300005;
int n,m,a[N],mn[N],mx[N],wa[N],wb[N],wv[N],wsu[N],sa[N],rk[N],he[N],l[N],r[N],f[N],s[N],top;
long long sm[N],ans[N];
char c[N];
vector<int>g[N];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>‘9‘||p<‘0‘)
    {
        if(p==‘-‘)
            f=-1;
        p=getchar();
    }
    while(p>=‘0‘&&p<=‘9‘)
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
bool cmp(int r[],int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void saa(char r[],int n,int m)
{
    int *x=wa,*y=wb;
    for(int i=0;i<=m;i++)
        wsu[i]=0;
    for(int i=1;i<=n;i++)
        wsu[x[i]=r[i]]++;
    for(int i=1;i<=m;i++)
        wsu[i]+=wsu[i-1];
    for(int i=n;i>=1;i--)
        sa[wsu[x[i]]--]=i;
    for(int j=1,p=1;j<=n&&p<n;j<<=1,m=p)
    {
        p=0;
        for(int i=n-j+1;i<=n;i++)
            y[++p]=i;
        for(int i=1;i<=n;i++)
            if(sa[i]>j)
                y[++p]=sa[i]-j;
        for(int i=1;i<=n;i++)
            wv[i]=x[y[i]];
        for(int i=0;i<=m;i++)
            wsu[i]=0;
        for(int i=1;i<=n;i++)
            wsu[wv[i]]++;
        for(int i=1;i<=m;i++)
            wsu[i]+=wsu[i-1];
        for(int i=n;i>=1;i--)
            sa[wsu[wv[i]]--]=y[i];
        swap(x,y);
        x[sa[1]]=1;
        p=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p:++p;
    }
    for(int i=1;i<=n;i++)
        rk[sa[i]]=i;
    for(int i=1,j,k=0;i<=n;he[rk[i++]]=k)
        for(k?k--:0,j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
}
int zhao(int x)
{
    return f[x]==x?x:f[x]=zhao(f[x]);
}
int main()
{
    scanf("%d%s",&n,c+1);
    for(int i=1;i<=n;i++)
        a[i]=read();
    saa(c,n,200);
    s[top=1]=1,l[1]=1;
    for(int i=2;i<=n;i++)
    {
        while(top&&he[s[top]]>he[i])
            top--;
        l[i]=top?s[top]+1:1;
        s[++top]=i;
    }
    s[top=1]=n,r[n]=n;
    for(int i=n-1;i>=1;i--)
    {
        while(top&&he[s[top]]>=he[i])
            top--;
        r[i]=top?s[top]-1:n;
        s[++top]=i;
    }
    // for(int i=1;i<=n;i++)
        // cerr<<he[i]<<endl;
    // for(int i=1;i<=n;i++)
        // cerr<<l[i]-1<<" "<<r[i]-1<<endl;
    ans[0]=-1e18;
    for(int i=1;i<=n;i++)
        g[he[i]].push_back(i),ans[i]=-1e18;
    for(int i=1;i<=n;i++)
        f[i]=i,mn[rk[i]]=mx[rk[i]]=a[i];
    // for(int i=1;i<=n;i++)
        // cerr<<sa[i]-1<<endl;
    for(int i=n-1;i>=1;i--)
    {
        sm[i]=sm[i+1],ans[i]=ans[i+1];
        for(int j=0,len=g[i].size();j<len;j++)
        {
            sm[i]+=1ll*(g[i][j]-l[g[i][j]]+1)*(r[g[i][j]]-g[i][j]+1);
            int fl=zhao(l[g[i][j]]-1),fr=zhao(r[g[i][j]]);
            ans[i]=max(ans[i],max(1ll*mx[fl]*mx[fr],1ll*mn[fl]*mn[fr]));
            f[fr]=fl;
            mx[fl]=max(mx[fl],mx[fr]);
            mn[fl]=min(mn[fl],mn[fr]);
        }
    }
    sort(a+1,a+1+n);
    ans[0]=max(1ll*a[1]*a[2],1ll*a[n]*a[n-1]);
    sm[0]=1ll*n*(n-1)/2;
    for(int i=0;i<n;i++)
        ans[i]=sm[i]?ans[i]:0;
    for(int i=0;i<n;i++)
        printf("%lld %lld\n",sm[i],ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/10347284.html

时间: 2024-11-05 04:16:46

bzoj 4199: [Noi2015]品酒大会【后缀数组+单调栈+并查集】的相关文章

BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )

求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ----------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ty

[UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集

[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 n杯鸡尾酒.这 n杯鸡尾酒排成一行,其中第 i杯酒 (1≤i≤n ) 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之一.设 Str(l,r)表示第 l杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字

【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集

[BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默水过~ 本题的实现还是非常简单的,先求出height数组,然后两杯酒'r'相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了.维护连续区间可以用并查集.统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,

[bzoj4199][Noi2015]品酒大会——后缀数组

题目大意: 给定一个序列,定义两个后缀是k相似的当且仅当这两个后缀有长度为k的公共前缀. 求对任意\(r\in [0,n-1]\),\(r\)相似的后缀的对数和两个后缀乘积的最大值. 思路: 先考虑后缀数组是如何计算两个后缀的lcp,发现是对于一段连续的height取min. 于是对于制定的相似度r,height < r的位置必定是两个后缀不能越过的,于是不难发现将有height \(\ge\)r的位置给取出来,然后整个序列分成了若干个连通块,一对具有r相似的后缀必定同时在一个联通快里面. 对于

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

BZOJ 4199 [Noi2015]品酒大会:后缀数组 + 并查集

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4199 题意: 给你一个长度为n的字符串s,和一个长为n的数组v. 对于每个整数r∈[0,n-1]: (1)问你有多少对后缀(suffix(i), suffix(j)),满足LCP(suffix(i), suffix(j)) >= r (2)输出mul[r] = max(v[i]*v[j]),其中i,j满足(1)的条件 题解: 先考虑第(1)问. 由于LCP只受连续的一段height中最小

【BZOJ-4199】品酒大会 后缀数组 + 并查集合并集合

4199: [Noi2015]品酒大会 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 436  Solved: 243[Submit][Status][Discuss] Description 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 i

HUID 5558 Alice&#39;s Classified Message 后缀数组+单调栈+二分

http://acm.hdu.edu.cn/showproblem.php?pid=5558 对于每个后缀suffix(i),想要在前面i - 1个suffix中找到一个pos,使得LCP最大.这样做O(n^2) 考虑到对于每一个suffix(i),最长的LCP肯定在和他排名相近的地方取得. 按排名大小顺序枚举位置,按位置维护一个递增的单调栈,对于每一个进栈的元素,要算一算栈内元素和他的LCP最大是多少. 如果不需要输出最小的下标,最大的直接是LCP(suffix(st[top]),  suff

【bzoj3238】[Ahoi2013]差异 后缀数组+单调栈

题目描述 输入 一行,一个字符串S 输出 一行,一个整数,表示所求值 样例输入 cacao 样例输出 54 题解 后缀数组+单调栈,几乎同 bzoj3879 的后半部分. 我明显是做题做反了... 这里还是说一下这道题的做法. 先用后缀数组求出height. 然后由于有LCP(a,c)=min(LCP(a,b),LCP(b,c))(rank[a]<rank[b]<rank[c]),所以我们只需要知道排名相邻的两个后缀的LCP,而这就是height数组的定义. 转化为子问题:给出n个数,求所有子