BZOJ 4199 NOI2015品酒大会

Problem

BZOJ
题意: i[1,n1] ∀ i ∈ [ 1 , n − 1 ] ,求最长公共前缀的长度为i的后缀有多少对,并求权值乘积最大为多少

Solution

这道题的前置题目应该是AHOI2013 差异

在parent树上,祖先表示了这个节点的后缀,那么两节点的lca,就是最长公共后缀。
那么考虑翻转原串,建出后缀自动机,拓扑排序之后再跑dp,注意由于权值可能为负,所以还要维护一个mn。注意在insert时新建的nq也要初始化mx和mn。

Code

#include <cstring>
#include <cstdio>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=600010;
const ll INF=(ll)2e18;
int n,lst=1,tot=1,val[maxn],pre[maxn],ch[maxn][26],l[maxn],sz[maxn];
int a[maxn],b[maxn];
ll cnt[maxn],mx[maxn],mn[maxn],ans1[maxn],ans2[maxn],out1[maxn],out2[maxn];
char s[maxn];
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
void insert(int c,int pos)
{
    int np=++tot,p=lst;
    lst=np;l[np]=l[p]+1;sz[np]=1;mn[np]=mx[np]=val[pos];
    for(;p&&!ch[p][c];p=pre[p]) ch[p][c]=np;
    if(!p) pre[np]=1;
    else
    {
        int q=ch[p][c];
        if(l[q]==l[p]+1) pre[np]=q;
        else
        {
            int nq=++tot;mx[nq]=-INF;mn[nq]=INF;
            l[nq]=l[p]+1;pre[nq]=pre[q];pre[q]=pre[np]=nq;
            memmove(ch[nq],ch[q],sizeof(ch[q]));
            for(;ch[p][c]==q;p=pre[p]) ch[p][c]=nq;
        }
    }
}
void top()
{
    for(rg int i=1;i<=tot;i++) b[l[i]]++,ans2[i]=-INF;
    for(rg int i=1;i<=n;i++) b[i]+=b[i-1];
    for(rg int i=1;i<=tot;i++) a[b[l[i]]--]=i;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    read(n);scanf("%s",s+1);
    for(rg int i=1;i<=n;i++) read(val[i]);
    for(rg int i=n;i;i--) insert(s[i]-'a',i),out2[i]=-INF;
    top();out2[0]=-INF;mx[1]=-INF;mn[1]=INF;
    for(rg int i=tot,p;i;i--)
    {
        p=a[i];
        ans1[pre[p]]+=(ll)sz[pre[p]]*sz[p];sz[pre[p]]+=sz[p];
        if(mx[pre[p]]^(-INF)) getmax(ans2[pre[p]],mx[pre[p]]*mx[p]);
        if(mn[pre[p]]^INF) getmax(ans2[pre[p]],mn[pre[p]]*mn[p]);
        getmax(mx[pre[p]],mx[p]);getmin(mn[pre[p]],mn[p]);
    }
    for(rg int i=1;i<=tot;i++) out1[l[i]]+=ans1[i],getmax(out2[l[i]],ans2[i]);
    for(rg int i=n-2;~i;i--) out1[i]+=out1[i+1],getmax(out2[i],out2[i+1]);
    for(rg int i=0;i<n;i++) printf("%lld %lld\n",out1[i],out1[i]?out2[i]:0);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值