首页 > 其他分享 >ZR 七连 Day 1 游记

ZR 七连 Day 1 游记

时间:2023-09-08 13:11:06浏览次数:39  
标签:5005 int sum 然后 ZR 七连 Day dp 式子

ZR 七连 Day 1 游记

游记篇

赛前搞笑事件

今天是第一场正睿,还是要 好好对待

$ 17:59:58 $ 还在吃饭

$ 17:59:59 $ 做出重要决定,先打着比赛,有空就吃一口包子

$ 18:00 $ 比赛开始

乐死

比赛开始了

先读一下第一题,发现比较简单,可以使用二维前缀和过掉,于是我写了一个代码,然后寄了

一顿调试,找了几个小错误,然后发现了一个大错,因为统计的是连续的两个方块的数量,所以在求子矩阵的时候需要切掉两排

切掉以后就过了,测了测大样例也过了

$ 18:30:14 $ 提交 $ T1 $

读了读 $ T2 $,然后就一顿吐槽出题人

想了半天,也不会 $ T2 $,然后就去看 $ T3 $

感觉是水题,一眼秒,然后看到数据范围 $ n \le 10^{18} $,当场就炸了,不过考虑到打朴素的暴力有 $ 40 $ 分,还是打了暴力,其实就是一个简单的 $ dp $,然后测了测大样例,过了

$ 19:35:17 $ 提交 $ T3 $

然后看了看 $ T4 $,可以打爆搜,然后就写了一个爆搜交了上去

$ 19:59:47 $ 提交 $ T4 $

预估分数:$ 100 + 0 + 40 + 20 = 160 $

事实上一分不差

之后就玩 $ florr.io $ 去了,还 $ 7 = 1 $ 出了粉 $ Wing $

题解篇

T1 雪豹

题目传送门

这道题的题目意思是寻找一个矩阵中所有的指定宽高的子矩阵中相邻的方块是不同颜色的数量大于 $ k $,颜色只有 $ 0, 1 $ 两种

然后我们一眼就发现是二维前缀和

我们设 $ sum_{i, j} $ 表示从 $ 1, 1 $ 到 $ i, j $ 中相邻方块不同颜色的数量

每次统计的时候只需要记录新的方块的左边和上边是不是不同的,如果是不同的就增加答案,即:

sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + (g[i][j - 1] != g[i][j]) + (g[i - 1][j] != g[i][j]);

然后枚举所有的起点,用前缀和判断是否合法,数量这么求:

int nw = sum[k][l] - sum[i - 1][l] - sum[k][j - 1] + sum[i - 1][j - 1];

但是会发现,我们多统计了一点东西,也就是说,我们截出子矩阵,没有断开和原来相连的部分

我们可以这么统计(只以横排为例):

用 $ hh_{i, j} $ 表示第 $ i $ 行与上一行对应方块颜色不同的数量,第二维是前缀和

然后我们在记录答案的时候,要减去这个东西:

nw -= hh[i][l] - hh[i][j - 1];

列也是一样的

然后提交一下就 $ AC $ 了

完整代码如下:

# include <bits/stdc++.h>

# define int long long

using namespace std;

int n, m, h, w, kk;

char g[5005][5005];

int sum[5005][5005], hh[5005][5005], ll[5005][5005];

signed main () {

    cin >> n >> m >> h >> w >> kk;

    for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) {

        cin >> g[i][j];

        g[i][j] = (g[i][j] == '1');

    }

    for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) {

        sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + (g[i][j - 1] != g[i][j] && j != 1) + (g[i - 1][j] != g[i][j] && i != 1);

        hh[i][j] = hh[i][j - 1] + (g[i][j] != g[i - 1][j]);

        ll[i][j] = ll[i - 1][j] + (g[i][j] != g[i][j - 1]);

    }

    int ans = 0;

    for (int i = 1; i <= n - h + 1; ++ i) for (int j = 1; j <= m - w + 1; ++ j) {

        int k = i + h - 1, l = j + w - 1;

        int nw = sum[k][l] - sum[i - 1][l] - sum[k][j - 1] + sum[i - 1][j - 1];

        if (i != 1) nw -= hh[i][l] - hh[i][j - 1];

        if (j != 1) nw -= ll[k][j] - ll[i - 1][j];

        if (nw >= kk) ++ ans;

    }

    cout << ans << endl;

    return 0;

}

T2 猞猁

每次可以在相邻的两个数中把两个数各减 $ 1 $,并且 $ 1 $ 和 $ n $ 相邻,求能否让所有的数都变成 $ 0 $

我们假设在第一个地方操作 $ x $ 次,第二个地方操作 $ a_2 - x $ 次,第三个地方操作 $ a_3 - a_2 + x $ 次,以此类推,第 $ n $ 个地方操作 $ a_n - a_{n - 1} + \dots + (-1)^{n + 1}x $ 次

然后我们发现,在 $ n $ 个地方操作的次数加上第一个地方操作的次数相加等于 $ a_1 $

然后我们就可以发现,如果 $ n $ 是偶数,我们就可以把式子写出来,$ x $ 被抵消了,判断左边和右边是否相等,输出 $ 1 $ 或者 $ 0 $ 即可

如果 $ n $ 是奇数,就把 $ x $ 解出来,然后判断是否在上下界里面,输出 $ 1 $ 或者 $ 0 $ 即可

接下来讲解细节

首先,我们用一个变量 $ b $ 存储 $ a_n - a_{n - 1} + \dots + (-1)^{n}a_2 $

循环,每次 $ b = a_i - b $,然后记录变量 $ k $ 判断正负,每次 $ k = -k $

然后推式子,偶数的情况如下:

$ a_n - a_{n - 1} + \dots + a_2 = b $

所以我们的式子变成了 $ b - x + x = a_1 $

即 $ b = a_1 $

然后我们只需要判断上面的式子是否满足就可以了

奇数的情况如下:

$ a_n - a_{n - 1} + \dots - a_2 + x + x = a_1 $

同样的,我们用 $ b $ 代替前面一坨,得到 $ b + 2x = a_1 $

可以求出 $ x $

接下来求上下界

首先对于每个 $ a_i $,我们需要考虑正负问题,如果当前 $ x $ 是负的,那么 $ x $ 能取到最大值就让式子为 $ 0 $,因为 $ x $ 越大,式子越小,反之,$ x $ 取到的最小值就让式子最大,式子的最大值不能超过 $ a_i $,所以取 $ b - a_i $

如果 $ x $ 是正的,那我们就考虑相反的问题,如果 $ x $ 越大,式子越大,最大不超过 $ a_i $,所以可以取 $ a_i - b $,最小的话,可以取 $ -b $

然后就可以切掉这道题了

完整代码如下:

# include <bits/stdc++.h>

# define int long long 

using namespace std;

int n, a[500005];

inline void solve () {

    cin >> n;

    for (int i = 1; i <= n; ++ i) cin >> a[i];

    int k = 1, b = 0, minx = 0, maxx = a[1];

    for (int i = 2; i <= n; ++ i) {

        k *= -1;

        b = a[i] - b;

        if (k == 1) {

            minx = max (minx, -b);

            maxx = min (maxx, a[i] - b);

        }

        else {

            maxx = min (maxx, b);

            minx = max (minx, b - a[i]);

        }

    }

    if (k == -1) {

        if (b == a[1]) puts ("1");

        else puts ("0");

    }

    else {

        int x = (a[1] - b) / 2;

        if (x * 2 == a[1] - b) {

            if (minx <= x && x <= maxx) puts ("1");

            else puts ("0");

        }

        else puts ("0");

    }

}

signed main () {

    int t; cin >> t; while (t --) solve ();

    return 0;

}

T3 土拨鼠

题意是有一个集合 $ S $,每次可以走集合里的步数,每从一个位置离开,就可以拿走任意一个萝卜,也可以不拿,每个萝卜都不同

求从 $ 1 $ 走到 $ n $ 有多少种不同的方案

很简单,我们可以考虑 dp,设 $ dp_i $ 表示走到位置 $ i $ 的方案,这里不统计在位置 $ i $ 的萝卜拿法

然后我们就考虑位置 $ i $ 有 $ i + 1 $ 种方案

这样我们可以写出转移:

$ dp_i = \sum_{j \in S} dp_{i - j} \times (i - j + 1) $

然后我们发现 $ n $ 很大,所以 dp 行不通,考虑矩阵快速幂

我们初始矩阵竖着排,最上面是 $ dp_i $,最下面是 $ dp_{i - 14} $

然后我们考绿把这个矩阵变成最上面是 $ dp_{i + 1} $

也就是说,第一行需要把能通过当前的 $ dp_j $ 转移到 $ dp_i $ 的数字的对应值设为对 $ 2027 $ 取模的值,之后后面所有的行都直接把行号减一的列对应值设为 $ 1 $,其他的全是 $ 0 $ 就可以了

这样我们得到了 $ 2027 $ 个矩阵,每次的话是轮流乘

这样我们就先把 $ 2027 $ 个矩阵的乘积求出来,然后看看有几个完整的,只有一个不完整的,这样复杂度就大大降低

代码如下:

# include <bits/stdc++.h>

# define int long long

using namespace std;

const int mod = 2027;

struct Mat {

    int a[20][20];

    Mat () {

        memset (a, 0, sizeof (a));

    }

    inline Mat operator * (const Mat b) const {

        Mat ans;

        for (int i = 1; i <= 15; ++ i) for (int j = 1; j <= 15; ++ j) for (int k = 1; k <= 15; ++ k) {

            (ans.a[i][j] += a[i][k] * b.a[k][j] % mod) %= mod;

        }

        return ans;

    }

    inline void init () {

        memset (a, 0, sizeof (a));

        for (int i = 1; i <= 15; ++ i) a[i][i] = 1;

    }

} tzf[3005] ;

int n, m; bool vis[35];

inline Mat ksm (Mat a, int b) {

    Mat ans;

    ans.init ();

    for (; b; b >>= 1, a = a * a) {

        if (b & 1) ans = ans * a;

    }

    return ans;

}


signed main () {

    cin >> n >> m;

    for (int i = 1; i <= m; ++ i) { int x; cin >> x; vis[x] = 1; }

    for (int k = 0; k <= mod - 1; ++ k) {

        for (int i = 1; i <= 15; ++ i) if (vis[i]) tzf[k].a[1][i] = k;

        for (int i = 2; i <= 15; ++ i) tzf[k].a[i][i - 1] = 1;

    }

    int s = n / mod, laz = n % mod;

    Mat sum;

    sum.init ();

    for (int i = 1; i <= mod; ++ i) sum = sum * tzf[i % mod];

    Mat ans; ans.a[1][1] = 1;

    ans = ans * ksm (sum, s);

    sum.init ();

    for (int i = 1; i <= laz; ++ i) sum = sum * tzf[i % mod];

    ans = ans * sum;

    cout << ans.a[1][1] << endl;

    return 0;

}

T4 芝士

我们考虑二分答案 $ k $,接下来考虑 check 怎么写

我们这么考虑,前两个随便放,然后其他的有一个范围,如果超出了范围就直接返回 $ 0 $

设大范围是 $ L, R $,小范围是 $ l, r $,初始大范围是左边,小范围是右边,如果两个范围都可以的话,就找一个最优的

我们可以把大范围设为两个区间合并后更大的范围,好像就是并,然后小范围就是当前的范围,也就是 $ [a_i - k, a_i + k] $

接下来如果只能满足一个范围,就直接放就可以了,并更新

都不满足就返回 $ 0 $

这是最简单的题,但是思维较难

代码如下:

# include <bits/stdc++.h>

using namespace std;

int n, a[1000005];

inline bool check (int k) {

    int l1 = a[1] - k, r1 = a[1] + k;

    int l2 = a[2] - k, r2 = a[2] + k;

    if (a[2] < l1 || a[2] > r1) return 0;

    for (int i = 3; i <= n; ++ i) {

        if (a[i] > r1 || a[i] < l1) {

            if (a[i] > r2 || a[i] < l2) return 0;

            l2 = a[i] - k, r2 = a[i] + k;

        }

        else if (a[i] > r2 || a[i] < l2) {

            l1 = a[i] - k, r1 = a[i] + k;

        }

        else {

            l1 = min (l1, l2);

            r1 = max (r1, r2);

            l2 = a[i] - k, r2 = a[i] + k;

        }

    }

    return 1;

}

signed main () {

    cin >> n; for (int i = 1; i <= n; ++ i) cin >> a[i];

    int l = 0, r = 1e9, ans = 0;

    while (l <= r) {

        int mid = (l + r) >> 1;

        if (check (mid)) ans = mid, r = mid - 1;

        else l = mid + 1;

    }

    cout << ans << endl;

    return 0;

}

标签:5005,int,sum,然后,ZR,七连,Day,dp,式子
From: https://www.cnblogs.com/Tzf-tzf/p/17683525.html

相关文章

  • 暑假集训Day19 比赛题解
    2023-08-0516:22:13总结这次打下来,由于T2贪心不够完全,T3模拟\(5\)个时不是最优,T4想到暴力做法但是来不及打,加之全都是捆绑测试点,导致我T2,T3虽然加起来有不少点对了,但是还是判全错,最后也只剩下T1的100。感觉这次前三题也不难,都是可做的,T4的30pts暴力也很白给,但......
  • 暑假集训 Day17 模拟赛题解
    2023-08-0318:18:03前言好家伙,很少完整订正一场比赛,可能是因为这个比赛相对来说确实不难吧(至少正解不难)。总结与反思这场比赛其实没有我想象的那么难,只是觉得题目可能不简单,就没有往简单的思路想,反而是被之前讲过的题疑惑,以为要用到一些很奇特的算法,结果打完以后看了题解再结......
  • java基础-java面向对象-day08
    1.一个简单的类认识类成员变量类方法publicclassPerson{//类的成员变量intage;Stringname;doubleheight;doubleweight;publicvoideat(){System.out.println("吃饭");}publicvoidsleep(){System.out......
  • Python crawler - Day1(PM)
    1.set_cookie.pyimportrequestsimportjson#百度句子翻译的URLurl="https://fanyi.baidu.com/basetrans"#要传递的post参数(注意替换为自己浏览器看到的token、sign值)data={"query":"happyeveryday","from":"en",&quo......
  • python-day2
    1.类型转换name='宁颂姝'age=1print('我叫'+name+',今年'+str(age)+'岁')a=2b=4.4c=Falseprint(type(a),str(a),type(str(a)))print(type(a),float(a),type(float(a)))print(type(b),int(b),type(int(b)))print(type(c),int(c),type(int......
  • Learn Git in 30 days——第 13 天:暂存工作目录与索引的变更状态
    写的非常好的一个Git系列文章,强烈推荐原文链接:https://github.com/doggy8088/Learn-Git-in-30-days/tree/master/zh-cn有没有遇过这种情境,某个系统开发写到一半,结果被老板或客戶「插单」,被要求紧急修正一个现有系统的Bug或添加一个功能,眼前的程序即将完成,老板的「急件」又不......
  • 苍穹外卖-Day01
    苍穹外卖-Day011.项目整体介绍1.1项目定位项目的定位:专门为餐饮企业(餐厅,饭店)定制的一款软件产品。项目主要分为两个端:(1)管理端:外卖商家使用。(2)服务端:点餐用户使用。1.2项目的功能架构项目的功能架构:体现项目的业务功能模块1.......
  • 华为认证数通考试真题DAY3
    1、在华为AR路由器中,缺省情况下OSPF协议优先级的数值为?()A.20B.10C.30D.02、在广播型的接口上配置静态路由时,必须要指定下一跳地址。()A.对B.错3、在OSPF广播网络中,一台DRother路由器会与哪些路由器交换链路状态信息?(多选)A.DRB.BDRC.所有OSPF邻居D.DROther4、直连路由协议优......
  • day24 - 回溯算法part01
    回溯算法理论基础 77. 组合classSolution{public:vector<vector<int>>result;vector<int>path;voiddfs(intn,intk,intstart){if(path.size()==k){result.push_back(path);return;}......
  • day1 - 数组part01
    力扣704.二分查找思路:假如有n个数,数组下标就是0到n-1,那么第一次从n/2开始找如果这个数比目标数大,说明目标数在左边,于是从0到中间边界找。如果这个数比目标数小,说明目标数在右边,于是从中间边界+1到n-1找。为了明确中间边界是多少,举个例子: 假如数组是:0,1,3,5,6,7,8;target......