首页 > 其他分享 >P8865 [NOIP2022] 种花 题解

P8865 [NOIP2022] 种花 题解

时间:2023-01-07 15:33:24浏览次数:70  
标签:read 题解 memset int P8865 1100 NOIP2022 sizeof define

P8865 [NOIP2022] 种花 Solution

目录

更好的阅读体验戳此进入

题面

大概就是在有障碍的网格图里分别问能填充多少 CF 形状的图形。严谨地叙述太复杂了,这里就不多叙述,直接去看 洛谷题面 吧。

然后顺便吐槽一下,这不愧是经典的 NOIP T1,感觉和上次的报数差不多,难度有点低了,比 CSP 的 T1 T2 都要简单,可惜考试取消了没考上。

Solution

做完之后翻了一下讨论区,似乎还有一些高妙的悬线法之类的解法,可以很简短地切掉这题。不过我不会我感觉不是很好像,所以这里提供一个思维难度极低,代码略长的做法,比较无脑,但是是妥妥的 $ O(n^2) $。

大概就是手画一下找性质,然后发现,对于 C 型来说,当我们固定其上下两行之后,若最左侧列从上至下都是 $ 0 $,那么这样的方案书就是上端横行向右最大延申的 $ 0 $ 乘上下端延伸的。换句话说,我们就是要枚举确定每个 C 的左侧的竖直的部分,然后把上下两侧可以延伸的乘起来加到方案里。

然后这东西是 $ O(n^3) $ 的,也就是枚举每个点然后再枚举竖直能延申多少。

然后我们发现这东西实际上是可以优化的,也就是说当我们确定一个竖直部分上端点所在行为 $ i $ 的时候,如果这个点竖直向下最长可以延申 $ \xi $,那么我们要的就是行数为 $ [i + 2, i + \xi] $ 之间的所有可能水平延伸的长度之和。

这样的话我们就随便做一个竖直方向上的前缀和即可优化 $ O(n^3) \longrightarrow O(n^2) $,则对于 C 型的就过了。

对于维护最长能延申的距离,随便写一个 deque 即可,也就是双端队列,里面存下标,对于每个值如果为 $ 0 $ 那么直接插进去,如果为 $ 1 $ 那么更新队列里所有的下标的值即可,具体可看代码,很好理解。

然后考虑 F 型,这个需要想一下,发现对于一个确定的 C 型,变成 F 型就是乘上其下端点能够延伸的距离。这个正常做还是 $ O(n^3) $ 的,优化方式也类似,类比之前的方式,维护水平延申长度的时候直接乘上竖直延申长度,然后再做个前缀和即可,具体实现可以参考代码。

Tips:有一些循环能压在一起,会让代码可读性变差这里就不压了。然后因为是 VP 也没打对拍,交上去最后两个点 WA 了,随便写了个满的数据才发现是最后加法没有取模。。。不过这种错误随便拍一下就能调出来,然后这题本身也很好调,思路非常直观,一步一步检查即可。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define MOD (ll)(998244353)

template < typename T = int >
inline T read(void);

int N, M, C, F;
bool mp[1100][1100];
int cline[1100][1100];
int srow[1100][1100];
int crow[1100][1100];
int line_x_row[1100][1100];
ll srowF[1100][1100];
ll cntC(0), cntF(0);

int main(){
    int T = read(); (void)read();
    while(T--){
        memset(mp, 0, sizeof mp);
        memset(cline, 0, sizeof cline);
        memset(srow, 0, sizeof srow);
        memset(crow, 0, sizeof crow);
        memset(line_x_row, 0, sizeof line_x_row);
        memset(srowF, 0, sizeof srowF);
        cntC = cntF = 0;
        N = read(), M = read(), C = read(), F = read();
        for(int i = 1; i <= N; ++i)
            for(int j = 1; j <= M; ++j){
                char c = getchar();
                while(c != '1' && c != '0')c = getchar();
                mp[i][j] = c - '0';
            }
        for(int i = 1; i <= N; ++i){//i = line, j - row
            deque < int > cur;
            for(int j = 1; j <= M; ++j)
                if(!mp[i][j])cur.emplace_back(j);
                else while(!cur.empty())cline[i][cur.front()] = cur.size() - 1, cur.pop_front();
            while(!cur.empty())cline[i][cur.front()] = cur.size() - 1, cur.pop_front();
        }
        // for(int i = 1; i <= N; ++i)for(int j = 1; j <= M; ++j)printf("%d%c", cline[i][j], j == M ? '\n' : ' ');
        for(int i = 1; i <= M; ++i){//i - row, j - line
            deque < int > cur;
            for(int j = 1; j <= N; ++j)
                if(!mp[j][i])cur.emplace_back(j);
                else while(!cur.empty())crow[i][cur.front()] = cur.size() - 1, cur.pop_front();
            while(!cur.empty())crow[i][cur.front()] = cur.size() - 1, cur.pop_front();
        }
        // for(int i = 1; i <= N; ++i)for(int j = 1; j <= M; ++j)printf("%d%c", crow[j][i], j == M ? '\n' : ' ');
        for(int i = 1; i <= M; ++i)//i - row, j - line
            for(int j = 1; j <= N; ++j)
                srow[i][j] = srow[i][j - 1] + cline[j][i];
        for(int i = 1; i <= M; ++i)
            for(int j = 1; j <= N; ++j)
                line_x_row[i][j] = crow[i][j] * cline[j][i] % MOD;
        for(int i = 1; i <= M; ++i)
            for(int j = 1; j <= N; ++j)
                srowF[i][j] = (srowF[i][j - 1] + line_x_row[i][j]) % MOD;
        for(int i = 1; i <= M; ++i)
            for(int j = 1; j <= N - 2; ++j){
                if(crow[i][j] < 2)continue;
                (cntC += (ll)cline[j][i] * (srow[i][j + crow[i][j]] - srow[i][j + 1]) % MOD) %= MOD;
                if(!crow[i][j + 2])continue;
                (cntF += (ll)cline[j][i] * (srowF[i][j + crow[i][j]] - srowF[i][j + 1]) % MOD) %= MOD;
            }
        printf("%lld %lld\n", cntC * C, cntF * F);
    }
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_11_28 初稿

标签:read,题解,memset,int,P8865,1100,NOIP2022,sizeof,define
From: https://www.cnblogs.com/tsawke/p/17032737.html

相关文章

  • 【题解】P4218 [CTSC2010]珠宝商
    这种题出出来有什么必要吗,不就是难写的暴力弱智题。题意给定一棵树和一个文本串\(T\),每个结点上有一个字符,问树上任意路径构成的字符串在\(T\)中的出现次数之和。\(n......
  • SDK更新不了问题解决
    问题阐述相信大家在更新SDK的时候都会遇到更新不了的问题,而且打不开Google搜索,这是因为天朝墙了Google,所以要么只能通过科学上网或者改HOSTS才能访问,更新SDK!本节来介绍两种......
  • 牛客小白月赛65 D题 题解
    原题链接题意描述一共有两堆石子,第一堆有\(a\)个,第二堆有\(b\)个,牛牛和牛妹轮流取石子,牛牛先手,每次取石子的时候只能从以下\(2\)种方案种挑一种来取(对于选择的方案......
  • NOI2003 文本编辑器 题解
    \STL大法好/正规解法块状链表,这里采取的是黑科技解法。rope是扩展STL库中的一个数据结构——可持久化平衡树,相比较set,它更适合区间插入和删除。这里用来解此题,就显得十......
  • CF1779C Least Prefix Sum 题解
    可能更好的阅读体验题目传送门题目大意给定一个序列\(a\),长度\(n\)。每次操作可以把\(a_i\)变成\(-a_i\)。要求\(a\)做前缀和之后的序列\(s\)中最小值为\(s......
  • CF1779D Boris and His Amazing Haircut 题解
    可能更好的阅读体验题目传送门题目翻译题目解析如果有\(a_i<b_i\)直接输出NO。我们发现:如果\(b_l=b_r=x\)并且所有的\(l\lei\ler\)都有\(b_i\lex\)那么......
  • CF1779A Hall of Fame 题解
    可能更好的阅读体验题目传送门题目翻译有\(n\)个纪念碑以及对应的\(n\)个台灯。给定一个由L和R组成的序列\(a\),代表台灯的朝向。如果第\(i\)个台灯为L,代......
  • 【题解】P4027 [NOI2007] 货币兑换
    题意好长,但是不想概括了。\cdq/!思路cdq分治+凸包。首先考虑令\(f_i\)表示第\(i\)天可以得到的最大金额,\(f_1=s\)因为\(rate_i\)是常数,并且保证存在最优方......
  • 【题解】CF1178G The Awesomest Vertex
    题意给定一棵大小为\(n\)的树以及\(m\)个操作,定义一个结点\(u\)的权值为:\(|\sum\limits_{w\inR(v)}a_w|\cdot|\sum\limits_{w\inR(v)}b_w|\)其中\(R(v)......
  • 230102模拟题解
    t1容易发现对于奇数和偶数,能满足条件的长度分别是单调的,所以可以分别二分答案检查。检查的时候对于差分序列做哈希即可,然后用set/map/哈希表判\(B\)的子段是否有\(A......