• 【真题T1】[NOIP2022] 种花


    一.题目

    P8865 [NOIP2022] 种花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


    二.思路(80pts)

    (1)"C"型

    • 则我们可以计算出每一行的前缀和,然后枚举每一列
    • 再每枚举每一行,定义为x1;
    • 在x1的基础上往下枚举,找出x2.则"C"型就成型了。

    (2)“F”型

    计算每一列的前缀和,就在“C“型的基础上,再乘以x2的列前缀和,即x2以下还有几个格子。

     (3)代码

    1. #include
    2. #define maxn 1005
    3. #define mod 998244353
    4. using namespace std;
    5. char s[maxn][maxn];
    6. int f[maxn][maxn],g[maxn][maxn]; //前缀和
    7. long long ansc,ansf;
    8. int main(){
    9. int T,id,n,m,ccc,fff;
    10. scanf("%d%d",&T,&id);
    11. while(T--){
    12. //初始化
    13. ansc=0; ansf=0;
    14. scanf("%d%d%d%d",&n,&m,&ccc,&fff);
    15. for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    16. //前缀和
    17. for(int i=1; i<=n;i++){
    18. f[i][m + 1]=0;
    19. for(int j=m;j>=1;j--) {
    20. f[i][j]=s[i][j]=='0' ? 1+f[i][j + 1] : 0;
    21. }
    22. }
    23. for(int j=1; j<=m;j++) {
    24. g[n+1][j]=0;
    25. for(int i=n; i>=1;i--) {
    26. g[i][j]=s[i][j]=='0' ? 1+g[i + 1][j] : 0;
    27. }
    28. }
    29. //work
    30. for(int j=1;j<=m;j++){ //枚举每一列
    31. for(int i=1;i<=n-2;i++){ //x1
    32. if(s[i][j]=='1') continue;
    33. for(int k=i+2;k<=n;k++){ //x2
    34. if(s[k-1][j]=='1') break;
    35. ansc = (ansc + max(0,f[i][j]-1) * max(0,f[k][j]-1) % mod) % mod;
    36. ansf = (ansf + max(0,f[i][j]-1) * max(0,f[k][j]-1) * max(0,g[k][j]-1) % mod) % mod;
    37. }
    38. }
    39. }
    40. printf("%lld %lld\n",ansc*ccc,ansf*fff);
    41. }
    42. return 0;
    43. }

    三.优化(100pts)

    我们可以看出,枚举列的循环显然是无法优化的。但我们枚举了两次行,即x1,x2。这我们可以优化。我们只枚举x2即可。

     

    我们发现,在枚举x2时,只需要x2*sum即可。sum就是x1的累加。


    四.AC

    1. #include
    2. #define maxn 1005
    3. #define mod 998244353
    4. using namespace std;
    5. char s[maxn][maxn];
    6. int f[maxn][maxn],g[maxn][maxn]; //前缀和
    7. long long ansc,ansf;
    8. int main(){
    9. int T,id,n,m,ccc,fff;
    10. scanf("%d%d",&T,&id);
    11. while(T--){
    12. //初始化
    13. ansc=0; ansf=0;
    14. scanf("%d%d%d%d",&n,&m,&ccc,&fff);
    15. for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    16. //前缀和
    17. for(int i=1; i<=n;i++){
    18. f[i][m + 1]=0;
    19. for(int j=m;j>=1;j--) {
    20. f[i][j]=s[i][j]=='0' ? 1+f[i][j + 1] : 0;
    21. }
    22. }
    23. for(int j=1; j<=m;j++) {
    24. g[n+1][j]=0;
    25. for(int i=n; i>=1;i--) {
    26. g[i][j]=s[i][j]=='0' ? 1+g[i + 1][j] : 0;
    27. }
    28. }
    29. //work
    30. for(int j=1;j<=m;j++){ //枚举每一列
    31. long long sum=0; //优化x1
    32. for(int i=3;i<=n;i++){
    33. if(s[i][j]=='1'){
    34. sum=0;
    35. i+=2;
    36. continue;
    37. }
    38. if(s[i-1][j]=='0') sum+=max(0,f[i-2][j]-1);
    39. ansc = (ansc + sum * max(0,f[i][j]-1) % mod ) %mod;
    40. ansf = (ansf + sum * max(0,f[i][j]-1) * max(0,g[i][j]-1) % mod ) %mod;
    41. }
    42. }
    43. printf("%lld %lld\n",ansc*ccc,ansf*fff);
    44. }
    45. return 0;
    46. }


    五.小问题

    Q:多测,这里为什么没有清空数组

    A:因为后者直接覆盖前者了。当然其中有一处和前者有联系。这里已经清空了。

  • 相关阅读:
    GBase 8s典型安装
    8月算法训练------第一天(数组)解题报告
    springboot闲置衣物捐赠系统毕业设计源码021009
    【生日快乐】SpringBoot SpringBoot 基础篇(第一篇) 第4章 SpringBoot 综合案例 4.7 修改客户功能
    C++学习笔记(十八)
    BiLSTM-CRF代码实现
    力扣二叉树--对称二叉树,从上向下打印二叉树刷题
    SpringBoot+jSerialComm实现Java串口通信 读取串口数据以及发送数据
    Atcoder Beginner Contest 294
    局域网监控软件如何防止数据泄密
  • 原文地址:https://blog.csdn.net/qq_49705495/article/details/133857063