【枚举&数据结构】【P2484】 [SDOI2011]打地鼠
Description
给定一个网格,每个格子上有一个数字。一次操作可以将 的一块矩形的数字减去 。必须保证这个矩形中的数全部为正。每次操作的 和 必须保持不变。求最少操作次数
Input
第一行是两个整数 ,代表格子行列数
下面 行 列描述这个格子
Output
一行一个数字代表答案
Hint
,数字和不超过
Solution
非常显然的想法是枚举 ,然后暴力的判断是否合法。枚举 的复杂度是 ,暴力判断的复杂度为 ,总复杂度 。
然后发现这个题目的特殊性质:行列不相关。即无论列选择多大,行的最优解是不变的。列得选择同理。证明上,可以考虑数学归纳法。显然选取列为 时能算出行的最优解。当选取列为 时的最优解已经算出时,考虑计算选取列为 时的最优解。我们发现如果列为 时是合法的,那么列为 时一定是合法的。同时这个条件也是必要条件。所以我们可以按照列选择 时枚举出行的最优解,行选择 时枚举出列得最优解。即可得到答案。
考虑检验是否合法时,可以使用二维树状数组维护差分从而支持区间减法。总复杂度 。可以通过本题。事实上,由于另一个方位是 ,所以可以直接维护一维树状数组做到 。然而要写两遍……
Code
#include <cstdio>
#include <cstring>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#define printtime()
#else
#include <ctime>
#define printtime() printf("Times used = %ld ms\n", clock())
#endif
#define ci const int
#define cl const long long
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
template <typename T>
inline void ReadDb(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
if (ch == '.') {
ch = IPT::GetChar();
double base = 1;
while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
}
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 110;
int n, m;
int MU[maxn][maxn], CU[maxn][maxn], tree[maxn][maxn];
inline int lowbit(ci x) {return x & -x;}
int check(ci, ci);
void insert(ci, ci, ci);
int ask(ci, ci);
int query(ci, ci);
int main() {
freopen("1.in", "r", stdin);
qr(n); qr(m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) qr(MU[i][j]);
int r = n, c = m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) CU[i][j] = MU[i][j] - MU[i - 1][j] - MU[i][j - 1] + MU[i - 1][j - 1];
while (check(r, 1) == -1) --r;
while (check(1, c) == -1) --c;
int sum = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) sum += MU[i][j];
qw(sum / (r * c), '\n', true);
printtime();
}
int check(ci x, ci y) {
int a = n - x + 1, b = m - y + 1, cnt = 0;
memset(tree, 0, sizeof tree);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
insert(i, j, CU[i][j]);
for (int i = 1; i <= a; ++i)
for (int j = 1; j <= b; ++j) {
int k = ask(i, j);
if (k < 0)
return -1;
cnt += k;
insert(i, j, -k); insert(i + x, j, k); insert(i, j + y, k); insert(i + x, j + y, -k);
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) if (ask(i, j) != 0)
return -1;
return cnt;
}
void insert(ci x, ci y, ci v) {
for (int i = x; i <= n; i += lowbit(i))
for (int j = y; j <= m; j += lowbit(j))
tree[i][j] += v;
}
int ask(ci x, ci y) {
return query(x, y) - query(x - 1, y) - query(x, y - 1) + query(x - 1, y - 1);
}
int query(ci x, ci y) {
int _ret = 0;
for (int i = x; i; i -= lowbit(i))
for (int j = y; j; j -= lowbit(j))
_ret += tree[i][j];
return _ret;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具