「AHOI2014/JSOI2014」拼图

「AHOI2014/JSOI2014」拼图

传送门
看到 \(n \times m \le 10^5\) ,考虑根号分治。
对于 \(n < m\) 的情况,我们可以枚举最终矩形的上下边界 \(tp, bt\),那么我们发现最终矩形一定是由所有满足从第 \(tp\) 行到第 \(bt\) 行都是白格子的矩形顺次连接,并且两端再各自接上一个最大的前缀和一个最大的后缀构成的。
这个我们可以 \(O(m)\) 地算。
总复杂度就是 \(O(n^2m)\),也就是一个根号级别的。
对于 \(n \ge m\) 的情况,我们肯定不能还去枚举上下边界,但是此时我们可以对于每一个白色的格子,都找一个它上面的最远的一个白格子来构成一组上下边界,然后套用第一问的计算方法就好了。
预处理是 \(O(nm)\) 的,总复杂度是 \(O(nm^2)\),还是一个根号级别的。
还有一个坑点就是再找前、后缀矩形时要避免重复使用一个矩阵,所以我们还得记录次大值。
参考代码:

#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
template < class T > inline T max(T a, T b) { return a > b ? a : b; }
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 1e5 + 5;

int s, n, m, l[_], r[_], lr[_], a[_], sum[_], up[_];
struct node { int first, second; } lmx, lmmx, rmx, rmmx;

inline int id(int i, int j) { return i != 0 && j != 0 ? (j - 1) * n + i : 0; }

inline int S(int x1, int y1, int x2, int y2) {
    return sum[id(x2, y2)] - sum[id(x2, y1 - 1)] - sum[id(x1 - 1, y2)] + sum[id(x1 - 1, y1 - 1)];
}

inline int calc(int tp, int bt) {
    int res = 0;
    for (rg int i = 1; i <= s; ++i)
        for (rg int ss = 0, j = l[i]; j <= r[i]; ++j) {
            if (S(tp, j, bt, j) != 0) ss = 0; else ++ss;
            res = max(res, (bt - tp + 1) * ss);
        }
    int mid = 0;
    lmx = lmmx = rmx = rmmx = (node) { 0, 0 };
    for (rg int i = 1; i <= s; ++i) {
        int ls = 0, rs = 0;
        for (rg int j = l[i]; j <= r[i]; ++j) if (S(tp, j, bt, j) != 0) break ; else ++ls;
        for (rg int j = r[i]; j >= l[i]; --j) if (S(tp, j, bt, j) != 0) break ; else ++rs;
        if (ls == lr[i]) mid += lr[i];
        else {
            if (ls > lmx.first) lmmx = lmx, lmx = (node) { ls, i };
            else if (ls > lmmx.first) lmmx = (node) { ls, i };
            if (rs > rmx.first) rmmx = rmx, rmx = (node) { rs, i };
            else if (rs > rmmx.first) rmmx = (node) { rs, i };
        }
    }
    if (lmx.second != rmx.second)
        res = max(res, (bt - tp + 1) * (lmx.first + mid + rmx.first));
    else {
        res = max(res, (bt - tp + 1) * (lmmx.first + mid + rmx.first));
        res = max(res, (bt - tp + 1) * (rmmx.first + mid + lmx.first));
    }
    return res;
}

inline void solve() {
    read(s), read(n), m = 0;
    for (rg int i = 1; i <= s; ++i) {
        read(lr[i]), l[i] = m + 1, m += lr[i], r[i] = m;
        for (rg int j = 1; j <= n; ++j)
            for (rg int k = l[i]; k <= r[i]; ++k) scanf("%1d", a + id(j, k));
    }
    for (rg int i = 1; i <= n; ++i)
        for (rg int j = 1; j <= m; ++j)
            sum[id(i, j)] = sum[id(i - 1, j)] + sum[id(i, j - 1)] - sum[id(i - 1, j - 1)] + a[id(i, j)];
    int ans = 0;
    if (n < m) {
        for (rg int i = 1; i <= n; ++i)
            for (rg int j = i; j <= n; ++j) ans = max(ans, calc(i, j));
    } else {
        for (rg int j = 1; j <= m; ++j) {
            for (rg int p = 0, i = 1; i <= n; ++i) {
                if (a[id(i, j)] != 0) {
                    for (rg int k = p + 1; k < i; ++k) up[id(k, j)] = p; p = i;
                }
                for (rg int k = p + 1; k <= n; ++k) up[id(k, j)] = p;
            }
        }
        for (rg int i = 1; i <= n; ++i)
            for (rg int j = 1; j <= m; ++j)
                if (a[id(i, j)] == 0) ans = max(ans, calc(up[id(i, j)] + 1, i));
    }
    printf("%d\n", ans);
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    int T; read(T);
    while (T--) solve();
    return 0;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12260647.html

时间: 2024-10-08 20:44:50

「AHOI2014/JSOI2014」拼图的相关文章

「AHOI2014/JSOI2014」骑士游戏

「AHOI2014/JSOI2014」骑士游戏 传送门 考虑 \(\text{DP}\). 设 \(dp_i\) 表示灭种(雾)一只编号为 \(i\) 的怪物的代价. 那么转移显然是: \[dp_i = \min(K_i, S_i + \sum_{j = 1}^{R_i} dp_{v_j})\] 但是我们会发现这个东西是有后效性的... 所以我们会想要用建图然后跑一个最短路什么的来搞... 于是我们观察到上面那个 \(\text{DP}\) 式子中,\(dp_i\) 如果用后面那一项来转移,显然

「AHOI2014/JSOI2014」支线剧情

「AHOI2014/JSOI2014」支线剧情 传送门 上下界网络流. 以 \(1\) 号节点为源点 \(s\) ,新建一个汇点 \(t\),如果 \(u\) 能到 \(v\),那么连边 \(u \to v\),下界为 \(1\),上界为 \(+\infty\),费用为对应的所需时间,表示这段剧情至少看一次,且看一次代价为对应的所需时间. 又因为我们可以在任何一个节点重开一次,所以我们的每个节点 \(u\) 都连边 \(u \to t\) ,下界为 \(0\),上界为 \(+\infty\),费

「JSOI2014」序列维护

「JSOI2014」序列维护 传送门 其实这题就是luogu的模板线段树2,之所以要发题解就是因为被 \(\color{black}{\text{M}} \color{red}{\text{_sea}}\) 告知了一种比较NB的 \(\text{update}\) 的方式. 我们可以把修改操作统一化,视为 \(ax + b\) 的形式,然后我们按照原来的套路来维护两个标记,分别代表 \(a\) 和 \(b\) ,那么我们的更新就可以这么写: inline void f(int p, int at

「JSOI2014」强连通图

「JSOI2014」强连通图 传送门 第一问很显然就是最大的强连通分量的大小. 对于第二问,我们先把原图进行缩点,得到 \(\text{DAG}\) 后,统计出入度为零的点的个数和出度为零的点的个数,两者取 \(\max\) 就是答案. 理性证明可以看这里 参考代码: #include <cstdio> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x&q

「JSOI2014」学生选课

「JSOI2014」学生选课 传送门 看到这题首先可以二分. 考虑对于当前的 \(mid\) 如何 \(\text{check}\) 我们用 \(f_{i,j}\) 来表示 \(i\) 对 \(j\) 的好感度排名,那么对于两个人 \(i\),\(j\) 如果有 \(\max\{f_{i, j}, f_{j, i}\} > mid\) 那么显然这两个人是不能上同一个老师的课的. 而且每个人可以上的课只有两种,我们记为 \(a_{i, 0 / 1}\) 假设 \(i\),\(j\) 对于当前的 \

「JSOI2014」打兔子

「JSOI2014」打兔子 传送门 首先要特判 \(k \ge \lceil \frac{n}{2} \rceil\) 的情况,因为此时显然可以消灭所有的兔子,也就是再环上隔一个点打一枪. 但是我们又会发现当 \(n = 3, k = 2\) 时,这种情况也满足上述条件但是我们只能打掉两群兔子,所以选兔子最多的两个格子打. 对于剩下的情况我们可以考虑 \(\text{DP}\) . 我们可以发现一件事,就是说如果我们把环弱化成链,那么顺着打就可以包含所有状态了. 所以说我们就可以有一个性质:两个

「JSOI2014」矩形并

「JSOI2014」矩形并 传送门 我们首先考虑怎么算这个期望比较好. 我们不难发现每一个矩形要和 \(n - 1\) 个矩形去交,而总共又有 \(n\) 个矩形,所以我们把矩形两两之间的交全部加起来再除以 \(n(n - 1)\) 就是答案. 至于算矩形之间的交我们可以考虑把每个矩形都视为在这个矩形范围内区间加上 \(1\) ,那么我们只需要查询一个矩形内的和 - 该矩形自身的贡献就可以算出一个矩形与其他矩形的交. 所以现在相当于我们只需要实现二维的区间加/查询. 但是数据范围很大我们不可能用

[luogu] P4040 [AHOI2014/JSOI2014]宅男计划(贪心)

P4040 [AHOI2014/JSOI2014]宅男计划 题目背景 自从迷上了拼图,JYY就变成了个彻底的宅男.为了解决温饱问题,JYY不得不依靠叫外卖来维持生计. 题目描述 外卖店一共有N种食物,分别有1到N编号.第i种食物有固定的价钱Pi和保质期Si.第i种食物会在Si天后过期.JYY是不会吃过期食物的. 比如JYY如果今天点了一份保质期为1天的食物,那么JYY必须在今天或者明天把这个食物吃掉,否则这个食物就再也不能吃了.保质期可以为0天,这样这份食物就必须在购买当天吃掉. JYY现在有M

怎样将「插件化」接入到项目之中?

本期移动开发精英社群讨论的主题是「插件化」,上网查了一下,发现一篇 CSDN 博主写的文章<Android 使用动态载入框架DL进行插件化开发>.此处引用原作者的话: 随着应用的不断迭代,应用的体积不断增大,项目越来越臃肿,冗余添加.项目新功能的加入,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,仅仅能紧急公布补丁版本号,强制用户进行更新.结果频繁的更新.反而easy减少用户使用黏性,或者是公司业务的不断发展,同系的应用越来越多,传统方式须要通过用户量最大的主项目进行引导下载并安装.