NOI 2015 品酒大会

NOI 2015 品酒大会
题意:戳这里
题解:这个一看就知道是后缀数组。。。。。我们得到hei数组之后从大到小排序,一个个加进去并统计答案,用并查集维护连续的一段,注意连续的一段要保留最小值(因为有负数)
代码:

#include <bits/stdc++.h>
#define For(ii,a,b) for (int ii=(a);ii<=(b);ii++)
#define Rof(ii,a,b) for (int ii=(a);ii>=(b);ii--)
#define rep(ii,a,b) for (int ii=(a);ii<=(b);ii++)
#define rek(ii,a,b) for (int ii=(a);ii>=(b);ii--)
#define Mem(a,t) memset(a,t,sizeof(a))
using namespace std;
const int maxL=300005;
const long long INF=(long long)1000000000*1000000000+4;
int N;
int rankk[maxL*2+1],sa[maxL],hei[maxL],s[maxL];
int w[maxL],m[maxL];
char ss[maxL];
///-----
int a[maxL],fa[maxL],minx[maxL],maxx[maxL],ll[maxL],rr[maxL];
bool bb[maxL];
struct point{
    int x,y;
    friend bool operator <(const point&a,const point&b ){
            return a.x>b.x;
    }
}pp[maxL];
bool campar(point a,point b){
    return a.x>b.x;
}
long long ans[maxL],sum[maxL];
///-----
void init(){
    scanf("%d",&N);
    scanf("%s",ss);
    rep(i,1,N) s[i]=ss[i-1];
    rep(i,1,N) scanf("%d",&a[i]);
}
void get_sa_hei(int *s,int N)
{
    Mem(rankk,0);
    For(i,0,maxL-1) w[i]=0;
    For(i,1,N) w[s[i]]++;
    For(i,1,maxL-1) w[i]+=w[i-1];
    Rof(i,N,1) m[w[s[i]]--]=i;
    rankk[m[1]]=1;
    For(i,2,N)
      if (s[m[i]]==s[m[i-1]]) rankk[m[i]]=rankk[m[i-1]];
         else rankk[m[i]]=rankk[m[i-1]]+1;
    for (int k=1; k<=N; k<<=1)
    {
        Mem(w,0);
        For(i,1,N) w[rankk[i+k]]++;
        For(i,1,N) w[i]+=w[i-1];
        Rof(i,N,1) m[w[rankk[i+k]]--]=i;
        Mem(w,0);
        For(i,1,N) w[rankk[i]]++;
        For(i,1,N) w[i]+=w[i-1];
        Rof(i,N,1) sa[w[rankk[m[i]]]--]=m[i];
        m[sa[1]]=1;
        For(i,2,N)
            if (rankk[sa[i]]==rankk[sa[i-1]] && rankk[sa[i]+k]==rankk[sa[i-1]+k])
                m[sa[i]]=m[sa[i-1]];
            else m[sa[i]]=m[sa[i-1]]+1;
        For(i,1,N) rankk[i]=m[i];
    }
    For(i,1,N) sa[rankk[i]]=i;
    for (int i=1,j=0; i<=N; i++)
    {
        if (rankk[i]==1) continue;
        for (j?j--:0; i+j<=N && sa[rankk[i]-1]+j<=N && s[sa[rankk[i]-1]+j]==s[i+j]; j++);
        hei[rankk[i]]=j;
    }
}
int getfa(int x){
    if (fa[x]==x) return x;
    fa[x]=getfa(fa[x]);
    return fa[x];
}
void solve(){
    rep(i,1,N) fa[i]=i,ll[i]=i,rr[i]=i;
    rep(i,1,N){
        pp[i].x=hei[i];
        pp[i].y=i;
    }
    sort(pp+1,pp+N+1);

    rep(i,0,N-1) ans[i]=-INF;
    rep(i,0,N-1) bb[i]=false;
    Mem(sum,0);
    rep(i,1,N) minx[i]=a[sa[i]],maxx[i]=a[sa[i]];
    rep(i,1,N){
        if (pp[i].y!=1)
        {
       //     printf("%d %d ",pp[i].x,pp[i].y);
            int t1=getfa(pp[i].y-1);
            int t2=getfa(pp[i].y);
            if ((long long)minx[t1]*minx[t2]>ans[pp[i].x]) {
                ans[pp[i].x]=(long long)minx[t1]*minx[t2];
            }
            if ((long long)maxx[t1]*maxx[t2]>ans[pp[i].x]) {
                ans[pp[i].x]=(long long)maxx[t1]*maxx[t2];
            }
            bb[pp[i].x]=true;
            sum[pp[i].x]+=(long long)(rr[t1]-ll[t1]+1)*(rr[t2]-ll[t2]+1);
            fa[t1]=t2;
            ll[t2]=ll[t1];
            minx[t2]=min(minx[t2],minx[t1]);
            maxx[t2]=max(maxx[t2],maxx[t1]);
        }
    }
    rek(i,N-1,0) sum[i]+=sum[i+1];
    rek(i,N-1,0)
    {
       if (bb[i+1]) ans[i]=max(ans[i+1],ans[i]),bb[i]=true;
    }
    rep(i,0,N-1){
        if (bb[i]) printf("%lld %lld\n",sum[i],ans[i]);else printf("0 0\n");
    }

}
int main(){
    init();
    get_sa_hei(s,N);
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值