【JZOJ4901】【NOIP2016提高A组集训第18场11.17】矩阵

题目描述

他是一名普通的农电工,他以一颗无私奉献的爱岗敬业之心,刻苦钻研业务,以娴熟的技术、热情周到的服务赢得了广大客户的尊敬和赞美。他就是老百姓称为“李电”的李春来。
众所周知,李电很喜欢YY。一天,他又YY 了奇怪的东西。他假设他自己成了神,然后他说:“出来吧,矩阵。”然后一个N _M 的矩阵从天而降。他为了不要让矩阵太大而使得自己眼花缭乱,所以他将M 固定在了3。但是,一天之后,他想继续他之前的YY,继续玩他的矩阵,但是他的记忆力太差了,所以他不记得他原来的矩阵长得什么样,所幸的是他昨天记录下了矩阵中每行每列的和,还有矩阵的每项都是非负整数。所以他想知道满足这样条件的矩阵总共有多少种。

数据范围

对于20% 的数据,满足n <= 3,0<=a,b,c,s_i<=10
对于40% 的数据,满足n<=10,0<=a,b,c,s_i<=20
对于60% 的数据,满足n<=40,0<=a,b,c,s_i<=40
对于100% 的数据,满足n<=125,0<=a,b,c,s_i<=125

=w=

设f[i][j][k]表示,第i行中第一列填个j,第二列中填个k。
f[i][j][k]=f[i1][ja][kb]并且满足a+b<=s[i]
然后咧,这个是O(n5)的。
考虑一个第i行:
这里写图片描述
根据转移方程,f[i][j][k]是由图中这个黑色的等腰直角三角形转移过来的。
那么考虑维护这个等腰直角三角形:
黑色等腰直角三角形=红色长方形-(黄色等腰直角三角形-青绿色等腰直角三角形)
显然矩形前缀和以及腰所在的直线在x轴的等腰直角三角形前缀和容易维护。


时间复杂度为O(n3)

代码

Const
        maxn=135;
        mo=100000000000000000;
Var
        n,i,j,k,l,o:longint;
        m1,m2,m3:longint;
        f:array[0..maxn,0..maxn,0..maxn] of int64;
        a,sum:array[0..maxn] of longint;
Function max(a,b:longint):longint;
begin
        if (a>b) then exit(a);
        exit(b);
end;
Begin
        assign(input,'mat.in');reset(input);
        assign(output,'mat.out');rewrite(output);
        readlN(n);
        readlN(m1,m2,m3);
        for i:=1 to n do
        begin
                read(a[i]);
                sum[i]:=sum[i-1]+a[i];
        end;
        if (sum[n]>m1+m2+m3) or (sum[n]<m1+m2+m3) then
        begin
                writeln(0);
        end
        else
        begin
                f[0][0][0]:=1;
                for i:=1 to n do
                begin
                        for j:=0 to m1 do
                                for k:=0 to m2 do
                                begin
                                        if (sum[i]<j+k) then break;
                                        if (sum[i]-j-k>m3) then continue;
                                        for l:=0 to j do
                                                for o:=max(0,j+k-a[i]-l) to k do
                                                begin
                                                        if (sum[i]-j-k>=sum[i-1]-l-o) then
                                                        begin
                                                                f[i][j][k]:=(f[i][j][k]+f[i-1][l][o]) mod mo;

                                                        end;
                                                end;
                                end;
                end;
                writeln(f[n][m1][m2]);
        end;
        close(input);close(output);
End.

=o=

当一个f[i]从f[i-1]转移过来时,可以画个平面直角坐标系,看看要维护的是什么。
有必要的话,建立空间直角坐标系。

原文地址:https://www.cnblogs.com/hiweibolu/p/6714824.html