题解 P8865 [NOIP2022] 种花

题解 P8865

鄙人没能去NOIP,赛后看的T1

此题按照题目思路一步一步进行模拟,最后思维优化一下,即可拿到 100 分的好成绩。

\(n^3\)的暴力大家肯定都会打,这里就不加以赘述了,看本题题解其他大佬的题解相信也可以理解。

(坑为 1 ,非坑为 0 ,图中的颜色稍后会进行解释。)

我们首先考虑如何进行优化,我们会发现,

如果条件满足 C 形,或者是 F 形,\(x_1,x_2\) 行上所有的 0 一定会对答案进行贡献,所以我们考虑记录在 a[i][j] 位置向右连续0 数量(因为CF 横着的部分都向右)。

memset(r,0ll,sizeof(r));
for(int i=1;i<=n;i++){
    for(int j=m;j>=1;j--){
        r[i][j]=a[i][j]==1?0:r[i][j+1]+1;
    }
}

因为图形 F\(x_2\) 位置以下到 \(x_3\) 的位置还会有连续的 0 ,所以我们要考虑这一部分应该如何优化。

举个例子,比如对于图中 \(x_1=1,x_2=3,y_0=1\) 的情况,我们会发现, \(x_2=3\) 以下的 60 都会对 F 的答案进行贡献,所以我们要记录 a[i][j] 及以下,连续0 的个数。

memset(d,0ll,sizeof(d));
for(int j=1;j<=m;j++){
    for(int i=n;i>=1;i--){
        d[i][j]=a[i][j]==1?0:d[i+1][j]+1;
    }
}

所有的初始化已经写好了,接下来考虑如何进行计算答案。

如果我们枚举 \(x_2\) 以及 \(y_0\) ,我们就可以通过刚刚初始化好的 r[],d[] 数组找到 \(x_3\) 以及 \(y_2\) ,但是如果想要计算最终的答案,我们还要找到 \(x_1\)\(y_1\) ,因为找到 \(x_1\) 也就可以找到对应的 \(y_1\) ,所以我们只需要思考如何找到 \(x_1\) 即可。

我们并没有初始化此部分,所以我们可以考虑在最终计算答案的时候记录下来。

看图,当我们枚举到 \(x_2=7,y_0=1\) 时,我们会发现,以上所有的行上的贡献都会加到此位置上,也就是说,我们需要记录下来,到位置 \(x_2\) 时,上面已经有多少贡献加到了此位置上,这个贡献稍微思考一下就会发现只有每一行的贡献加到了此处,所以我们在枚举的时候,可以将每一行的贡献(也就是 r[i][j]) 加入一个变量 sum ,计算时行上的贡献直接由 sum 计入即可。

这里如果中间的连续 0 断开的话, sum 就应该清零。

int sum=0ll,C=0ll,F=0ll;
for(int j=1;j<=m;j++){
    for(int i=3;i<=n;i++){
        if(a[i][j]){
            sum=0ll;
            i+=2ll;
            continue ;
        }
        if(!a[i-1][j]){
            if(r[i-2][j]>=1){
                sum+=r[i-2][j]-1;
            }
        }
        C=(C+sum*max(0ll,r[i][j]-1)%p)%p;
        F=(F+sum*max(0ll,r[i][j]-1)*max(0ll,d[i][j]-1)%p)%p;
    }
    sum=0ll;
}

最终代码

// #Tyrue#
#include<map>
#include<cmath>
#include<cstdio>
#include<string>
#include<iostream>
#include<string.h>
#include<algorithm>
#define int long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int N=1e3+10,p=998244353;
int T,n,m,c,f;
int a[N][N],r[N][N],d[N][N];
signed main(){
    T=read(),n=read();
    while(T--){
        n=read(),m=read(),c=read(),f=read();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%1lld",&a[i][j]);
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=m;j>=1;j--){
                r[i][j]=a[i][j]==1?0:r[i][j+1]+1;
            }
        }
        for(int j=1;j<=m;j++){
            for(int i=n;i>=1;i--){
                d[i][j]=a[i][j]==1?0:d[i+1][j]+1;
            }
        }
        int sum=0ll,C=0ll,F=0ll;
        for(int j=1;j<=m;j++){
            for(int i=3;i<=n;i++){
                if(a[i][j]){
                    sum=0ll;
                    i+=2ll;
                    continue ;
                }
                if(!a[i-1][j]){
                    if(r[i-2][j]>=1){
                        sum+=r[i-2][j]-1;
                    }
                }
                C=(C+sum*max(0ll,r[i][j]-1)%p)%p;
                F=(F+sum*max(0ll,r[i][j]-1)*max(0ll,d[i][j]-1)%p)%p;
            }
            sum=0ll;
        }
        memset(r,0ll,sizeof(r));
        memset(d,0ll,sizeof(d));
        printf("%lld %lld\n",c*C,f*F);
    }
    return 0;
}
posted @ 2022-12-03 18:45  Tyrue  阅读(162)  评论(1编辑  收藏  举报