codeforces 616F

原题

原题链接

题目大意

给你 n105 个字符串,字符串总长度 5105 ,每个字符串有一个价值 ci ,让你构造一个字符串,使得 ni=1cips,i|s| 最大, s 为构造的字符串,|s|为构造的字符串的长度, ps,i 为构造的字符串在第 i 个字符串中的出现次数。输出这个最大值。

解题思路

把字符串全部拼在一起,两两之间插入一个没有出现过的字符,然后做后缀数组。对于连续的一段height,答案就是 ciheight ,我可以记录每个 height 往左往右可以到扩展到哪里,这个可以用单调栈。

参考代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxn 600005
#define ll long long
using namespace std;

int s[maxn],sa[maxn],rank[maxn],wss[maxn],wv[maxn],x[maxn],y[maxn];

int cost[maxn],l[maxn],r[maxn],height[maxn],thlen[maxn],belong[maxn];

ll sum[maxn],ans;

int n,len;

char ch[maxn];

int nowlen;

int st[maxn];

void Read(){
    nowlen=0;
    char c=getchar();
    while (c<'a' || c>'z') c=getchar();
    while (c>='a' && c<='z') {
        ch[++nowlen]=c;
        c=getchar();
    }
}

void get_height() {
    fo(i,1,len) rank[sa[i]]=i;
    int k=0,j;
    for(int i=1;i<=len;height[rank[i++]]=k)
        for(k ? k-- : k,j=sa[rank[i]-1];s[i+k]==s[j+k];k++);
}

bool compare(int *rank,int i,int j,int k){
    return rank[i]==rank[j] && rank[i+k]==rank[j+k];
}

void SA() {
    int i,j,p,m='z'+n+1;
    fo(i,1,len) wss[x[i]=s[i]]++;
    fo(i,1,m) wss[i]+=wss[i-1];
    fo(i,1,len) sa[wss[x[i]]--]=i;
    for(j=1,p=0;p<len;m=p,j*=2) {
        for(p=0,i=len-j+1;i<=len;i++) y[++p]=i;
        for(i=1;i<=len;i++) if (sa[i]>j) y[++p]=sa[i]-j;
        for(i=1;i<=len;i++) wv[i]=x[y[i]];
        for(i=1;i<=m;i++) wss[i]=0;
        for(i=1;i<=len;i++) wss[wv[i]]++;
        for(i=1;i<=m;i++) wss[i]+=wss[i-1];
        for(i=len;i>=1;i--) sa[wss[wv[i]]--]=y[i];
        swap(x,y);
        for(x[sa[1]]=1,p=1,i=2;i<=len;i++)
            x[sa[i]]=compare(y,sa[i-1],sa[i],j) ? p : ++p;
    }
}

int main(){
    scanf("%d",&n);
    fo(i,1,n) {
        Read();
        int tmp=nowlen;
        fo(j,1,nowlen) {
            s[++len]=ch[j]-'a'+1;
            thlen[len]=tmp;
            tmp--;
            belong[len]=i;
        }
        s[++len]=i+'z';
        thlen[len]=0;
        belong[len]=n+1;
    }
    fo(i,1,n) scanf("%d",&cost[i]);
    SA();
    get_height();
    fo(i,1,len) sum[i]=sum[i-1]+cost[belong[sa[i]]];
    fo(i,1,len) {
        int tmp1,tmp2;
        if (i==1) tmp1=0; else tmp1=height[i];
        if (i==n) tmp2=0; else tmp2=height[i+1];
        if (thlen[sa[i]]>max(tmp1,tmp2)) 
            ans=max(ans,1ll*cost[belong[sa[i]]]*thlen[sa[i]]);
    }
    st[0]=0;
    fo(i,1,len) {
        while (st[0]>0 && height[st[st[0]]]>=height[i]) st[0]--;
        if (st[0]==0) l[i]=1; else l[i]=st[st[0]];
        st[++st[0]]=i;
    }
    st[0]=0;
    fd(i,len,1) {
        while (st[0]>0 && height[st[st[0]]]>=height[i]) st[0]--;
        if (st[0]==0) r[i]=len; else r[i]=st[st[0]];
        st[++st[0]]=i;
        ll now=sum[r[i]-1]-sum[l[i]-1];
        ans=max(ans,now*height[i]);
    }
    printf("%I64d",ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值