NOI2015品酒大会

跑一遍SAM,然后建一棵线段树,维护最大值和出现次数即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long LL;
inline LL C2(LL x){
    return (x * (x - 1)) >> 1;
}
const int ALB = 26;
const int maxn =300005;
char c[maxn],d[maxn];
static int cc[maxn];
struct pi{
    int le,ri;
    long long m,s;
}pp[maxn<<2];
void build(int tot,int l,int r){
    pp[tot].le=l;
    pp[tot].ri=r;
    pp[tot].m=-(LL)1<<60;
    pp[tot].s=0;
    if(l==r) return;
    build(2*tot,l,(l+r)/2);
    build(2*tot+1,(l+r)/2+1,r);
}
void merg1(int tot,int l,int r,long long m){
    if(pp[tot].le>=l&&pp[tot].ri<=r){
        pp[tot].m=max(m,pp[tot].m);
        return;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    if(l<=mid) merg1(2*tot,l,r,m);
    if(r>mid){
        merg1(2*tot+1,l,r,m);
    }
}
void merg2(int tot,int l,int r,long long s){
    if(pp[tot].le>=l&&pp[tot].ri<=r){
        pp[tot].s+=s;
        return;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    if(l<=mid) merg2(2*tot,l,r,s);
    if(r>mid){
        merg2(2*tot+1,l,r,s);
    }
}
long long query1(int tot,int x){
    if(pp[tot].le==pp[tot].ri){
        return pp[tot].m;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    long long s=pp[tot].m;
    if(x<=mid) return max(s,query1(2*tot,x));
    else return max(s,query1(2*tot+1,x));
}
long long query2(int tot,int x){
    if(pp[tot].le==pp[tot].ri){
        return pp[tot].s;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    long long s=pp[tot].s;
    if(x<=mid) return s+query2(2*tot,x);
    else return s+query2(2*tot+1,x);
}
struct NODE{
    NODE *par, *go[ALB];
    int val;
    int x;
    int y;
    int x1,y1;
    int size;
    NODE(int val):par(NULL),val(val),size(1){
        x=-1000000000;
        y=-1000000000;
        x1=1000000000;
        y1=1000000000;
        fill_n(go, ALB, (NODE*)NULL);
    }
    NODE(){};
}node[maxn<<1], *last,*root,*no;
int ncnt;
void Init(){
    memset(cc,0,sizeof(cc));
    ncnt = 1;
    root=&node[0];
    *root=NODE(0);
    last=root;
}
int a[maxn];
void Append(int x,int qq){
    NODE *p = last, *np = &node[ncnt ++];
    *np = NODE(p->val + 1);
    np->x=qq;
    np->x1=qq;
    for(; p && !p->go[x]; p = p->par)p->go[x] = np;
    if(p){
        NODE *q = p->go[x];
        if(p->val + 1 == q->val){
            np->par = q;
        }
        else{
            NODE *nq = &node[ncnt ++];
            *nq = *q;
            nq->size = 0;
            nq->val = p->val + 1;
            nq->x=nq->y=-1000000000;
            nq->x1=nq->y1=1000000000;
            q->par = nq;
            np->par = nq;
            for(; p && p->go[x] == q; p = p->par)
                p->go[x] = nq;
        }
    }
    else{
        np->par = root;
    }
    last = np;
}
long long s;
void Deal(int N,int u){
    int i;
    NODE *cur, *p;
    static NODE* top[maxn<<1];
    for(i = 0; i < ncnt; i ++)
        cc[node[i].val] ++;
    for(i = 1; i <= N; i ++)
        cc[i] += cc[i - 1];
    for(i = 0; i < ncnt; i ++)
        top[-- cc[node[i].val]] = &node[i];
    for(i = ncnt - 1; i > 0; i --){
        cur = top[i];
        p = cur->par;
        p->size += cur->size;
        int x=min(p->x,cur->x);
        p->x=max(p->x,cur->x);
        p->y=max(p->y,max(x,cur->y));
        x=max(p->x1,cur->x1);
        p->x1=min(p->x1,cur->x1);
        p->y1=min(p->y1,min(x,cur->y1));
    }
    for(i=1;i<ncnt;i++){
        cur = top[i];
        if(cur->size>1){
            int q=0;
            if(cur->par!=NULL) q=cur->par->val;
            merg1(1,q+1,cur->val,max((LL)cur->x*cur->y,(LL)cur->x1*cur->y1));
            merg2(1,q+1,cur->val,(LL)cur->size*(cur->size-1)/2);
        }
    }
}
int b[maxn];
int fk[26],fq[26],ff[26];
int main()
{
    int i,n;
    cin>>n;
    scanf("%s",d);
    for(i=0;i<n;i++) c[i]=d[n-1-i];
    Init();
    for(i=0;i<n;i++){
        scanf("%d",&b[i]);
    }
    for(i=0;i<n;i++) a[i]=b[n-1-i];
    for(i=0;i<n;i++){
        Append(c[i]-'a',a[i]);
    }
    s=0;
    build(1,1,n);
    Deal(n,0);
    long long s=0,s2;
    sort(a,a+n);
    s=(LL)n*(n-1)/2;
    if(n==1) s2=0;
    else s2=max((LL)a[n-1]*a[n-2],(LL)a[0]*a[1]);
    if(s2==-(LL)1<<60) s2=0;
    cout<<s<<" "<<s2<<endl;
    for(i=2;i<=n;i++){
        long long s,s1;
        s1=query1(1,i-1);
        s=query2(1,i-1);
        if(s1==-(LL)1<<60) s1=0;
        printf("%lld %lld\n",s,s1);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值