[BZOJ4199] [Noi2015]品酒大会

传送门

http://www.lydsy.com/JudgeOnline/problem.php?id=4199

题目大意

iai
lcp(i,j)(ij)<=1..n1max{aiaj}

题解

第一问
我们先求 lcp(i,j)=k 的对数
对原串的反串建立SAM得到后缀树,后缀树上点表示,原串的前缀串的逆序, lcalcp ,树形DP算下贡献即可
第二问
记录每个节点的子树中的max,min值树形DP一下
注意intmax要设到很大大

uses math;
const
    maxn=500005;
    maxint=4611686018427387904;
type
    data=record
    fa,len:longint; size,ans1,ans2,maxx,minn:int64;
    tranc:array[0..26]of longint;
    end;
var
    x:array[0..2*maxn]of data;
    w:array[0..4*maxn,1..2]of longint;
    y,ans1,ans2:array[0..4*maxn]of int64;
    i,j,k:longint;
    n,m,tail,tot,len,a,b,pre,num,tt:longint;
    sum:int64;
    st:ansistring;
procedure init(a,b:longint);
begin  
    w[len,1]:=b;
    if w[a,2]=0 then w[a,2]:=len else w[w[a,1],2]:=len;
    w[a,1]:=len; inc(len);
end;

procedure insert(a:longint;b:int64);
var p,np,q,nq:longint;
begin
    inc(tot); np:=tot; p:=tail;
    x[np].len:=x[p].len+1; x[np].size:=1; x[np].maxx:=b; x[np].minn:=b; x[np].ans2:=-maxint; x[np].ans1:=0;
    while (p<>0)and(x[p].tranc[a]=0) do
        begin
            x[p].tranc[a]:=np;
            p:=x[p].fa;
        end;
    if x[p].tranc[a]=0
    then x[p].tranc[a]:=np
    else
        begin  
            q:=x[p].tranc[a];
            if x[q].len=x[p].len+1
            then x[np].fa:=q
            else   
                begin
                    inc(tot); nq:=tot; x[nq]:=x[q]; x[nq].size:=0; x[nq].maxx:=-maxint; x[nq].minn:=maxint;
                    x[q].fa:=nq; x[np].fa:=nq;
                    x[nq].len:=x[p].len+1;
                    while x[p].tranc[a]=q do
                        begin
                            x[p].tranc[a]:=nq;
                            p:=x[p].fa;
                        end;
                end;
        end;
    tail:=np;
end;

procedure dfs(a:longint);
var tt:longint;
begin
    tt:=w[a,2];
    while tt<>0 do
        begin
            dfs(w[tt,1]);
            x[a].ans1:=x[a].ans1+x[a].size*x[w[tt,1]].size;
            x[a].size:=x[a].size+x[w[tt,1]].size;
            if x[a].maxx<>-maxint then x[a].ans2:=max(x[a].ans2,x[a].maxx*x[w[tt,1]].maxx);
            if x[a].minn<>maxint then x[a].ans2:=max(x[a].ans2,x[a].minn*x[w[tt,1]].minn);
            x[a].maxx:=max(x[a].maxx,x[w[tt,1]].maxx);
            x[a].minn:=min(x[a].minn,x[w[tt,1]].minn);
            tt:=w[tt,2];
        end;
end;

begin
    readln(n);
    readln(st);
    for i:=1 to n do
        read(y[i]);
    x[0].minn:=maxint; x[0].maxx:=-maxint; x[0].size:=0; x[0].ans2:=-maxint;
    for i:=n downto 1 do
        insert(ord(st[i])-96,y[i]);
    len:=tot+1;
    for i:=1 to tot do
        init(x[i].fa,i);
    dfs(0);
    for i:=0 to n do
        ans2[i]:=-maxint;
    for i:=0 to tot do
        begin ans1[x[i].len]:=ans1[x[i].len]+x[i].ans1; ans2[x[i].len]:=max(ans2[x[i].len],x[i].ans2); end;
    for i:=n-1 downto 0 do

        begin ans1[i]:=ans1[i]+ans1[i+1]; if ans1[i+1]<>0 then ans2[i]:=max(ans2[i],ans2[i+1]); end;
    for i:=0 to n-1 do
        begin
            if ans2[i]=-maxint then ans2[i]:=0;
            writeln(ans1[i],' ',ans2[i]);
        end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值