P2197 [JZOJ/中山市市选] 三核苷酸

3d36e83a3257229df7f58.png

考虑 \(\sum (N_i-mval)^2=\sum {N_i}^2-2\times N_i \times mval+mval^2\),我们用一个 \(tot\) 记录贡献个数,用 \(val\) 记录贡献和,\(ans\) 为每一个贡献的方的和。

由于答案是每一个两两的贡献,我们再定义 \(queue_{a,b,c,t}\) 代表 \(a,b,c\) 这种块的第 \(t\) 个出现的位置存一下。\(match_{a,b,c,t}\) 代表 \(queue_{a,b,c,t}-queue_{a,b,c,t-1}\)。用 \(tail_{a,b,c}\) 代表 \(queue\) 的指针。

首先 \(queue_{a,b,c,t}\)\(match_{a,b,c,t}\) 的转移十分的显然。

但是仅仅这样是不够的,我们设 \(sum_{a,b,c}\) 代表这种块的贡献的后缀和的和。设 \(square_{a,b,c}\) 代表这种块的贡献的后缀和后的每一个数的平方的和

比如:

6bc6c0c25ea23442c972c.png

\(tail_{a,b,c}=\) 3

\(queue_{a,b,c}=\) i | j | k

\(match_{a,b,c}=\) x | y 或者说 j-i | k-j

\(sum_{a,b,c}=\) x+2*y (x+y | y)

\(square_{a,b,c}=\) (x+y)^2+y^2

现在我们来看一下答案的更新。(我们用 \(X\) 代表 \(tail_{a,b,c}-1\),\(tmp\) 代表 \(match_{a,b,c}\))

\[tot+=X\]

\[val+=sum_{a,b,c}\]

\[ans+=square_{a,b,c}+2 \times tmp*sum_{a,b,c}+X*tmp^2\]

更新的时候也很简单

\[square_{a,b,c}+=tmp^2*X+2 \times tmp*sum_{a,b,c}\]

\[sum+=tmp*X\]

神奇 \(60\) 分,公式没有问题...

Const
    total=100010;

var
    match,queue:array[1..4,1..4,1..4,-1..total] of longint;
    sum,tail,square:array[1..4,1..4,1..4] of longint;
    num:array[-1..total] of longint;
    i,j,k,n,m,a,b,c,tmp,test:longint;
    val,tot,ans:int64;
    exist:boolean;
    s:ansistring;

// match 表示上一个与此相同的字符串的位置差,queue 表示与此字符串相同的位置集合
// sum 表示 match 的后缀和的和,square 为 match 的后缀和的平方和
// tot 为个数,val 是和,ans 为平方和

procedure Work;
var len:longint;
begin
    fillchar(square,sizeof(square),0);
    fillchar(queue,sizeof(queue),0);
    fillchar(match,sizeof(match),0);
    fillchar(tail,sizeof(tail),0);
    fillchar(sum,sizeof(sum),0);
    fillchar(num,sizeof(num),0);
    readln(s); len:=length(s);
    ans:=0; tot:=0; val:=0; exist:=True;
    for i:=1 to len do
        Case s[i] of
            'A' : num[i]:=1; 'G' : num[i]:=2; 'C' : num[i]:=3; 'T' : num[i]:=4;
        end;
    for i:=1 to len-2 do
    begin
        a:=num[i]; b:=num[i+1]; c:=num[i+2];
        inc(tail[a,b,c]); queue[a,b,c,tail[a,b,c]]:=i;
        if tail[a,b,c]=1 then continue;
        match[a,b,c,tail[a,b,c]]:=i-queue[a,b,c,tail[a,b,c]-1]+1;
        tmp:=match[a,b,c,tail[a,b,c]]; dec(tail[a,b,c]);
        inc(ans,square[a,b,c]+tmp*(sum[a,b,c] << 1)+tail[a,b,c]*sqr(tmp)); 
        inc(square[a,b,c],sqr(tmp)*tail[a,b,c]+tmp*(sum[a,b,c] << 1));
        inc(sum[a,b,c],tmp*tail[a,b,c]);
        inc(val,sum[a,b,c]); inc(tot,tail[a,b,c]); inc(tail[a,b,c]);
    end;
    for i:=1 to 4 do for j:=1 to 4 do for k:=1 to 4 do if tail[i,j,k]>1 then exist:=False;
    if exist then begin writeln('0.000000'); exit; end; writeln(ans,' ',val,' ',tot);
    writeln(ans/tot-sqr(val/tot):0:6);
end;

begin
    {assign(input,'tri.in');reset(input);
    assign(output,'tri.out');rewrite(output);}
    readln(test);
    while test>0 do begin Work; dec(test); end;
    close(input); close(output);
end.

转载于:https://www.cnblogs.com/FibonacciHeap/articles/10701216.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值