【vijos】1750 建房子(线段树套线段树+前缀和)

https://vijos.org/p/1750

是不是我想复杂了。。。。

自己yy了个二维线段树,然后愉快的敲打。

但是wa了两法。。。。。。。sad

原因是在处理第二维的更新出现了个小问题,sad。

void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); }

这里注意是mm*4。。。我该好好想想了。。这是在dbg的时候找出来的问题。sad。

我觉得很奇怪,线段树的底层节点一共就mm个,那么整棵树不就是mm*2-1个节点吗。为嘛要开到*4。。。。求教。。

这题很明显,我们枚举每一个点然后维护矩阵的最小值,然后前缀和剪掉就行了。

维护矩阵用线段树套线段树,log^2n,时间感人的到了5000ms。。。。sad。。也就是维护个行区间,然后维护个列区间。

我第一次写,而且是自己yy的,不知道会不会有什么常数优化?

还有听说这题是单调队列?what?

套线段树自己yy一下就能出来了的,我就不说了。。不会就看代码吧。

然后计算的时候应该开longlong吧

然后在选的时候标记其实很简单,只需要考虑四个点有没有被取掉就行了,理由很简单。。。。。。。。。自己想吧。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
#define printarr1(a, b) for1(_, 1, b) cout << a[_] << ‘\t‘; cout << endl
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }

#define lc x<<1
#define rc x<<1|1
#define lson l, m, lc
#define rson m+1, r, rc
#define MID (l+r)>>1

typedef long long ll;
const int N=1005, oo=0x7f7f7f7f;
struct dat { int x, y; ll d; }home[N*N], ans[N*N];
bool cmp(const dat &a, const dat &b) {
	if(a.d==b.d) {
		if(a.x==b.x) return a.y<b.y;
		return a.x<b.x;
	}
	return a.d<b.d;
}
int mn[N<<2][N<<2], a[N][N], A, B, cnt, ct, nn, mm;
bool vis[N][N];
ll sum[N][N];

void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); }
void pushup2(int *s, int x) { s[x]=min(s[lc], s[rc]); }
void build2(int l, int r, int x, int *s, int row) {
	if(l==r) { s[x]=a[row][l]; return; }
	int m=MID;
	build2(lson, s, row); build2(rson, s, row);
	pushup2(s, x);
}
void build1(int l, int r, int x) {
	if(l==r) { build2(1, mm, 1, mn[x], l); return; }
	int m=MID;
	build1(lson); build1(rson);
	pushup1(x);
}
int query2(int l, int r, int x, int *s, int L, int R) {
	if(L<=l && r<=R) return s[x];
	int m=MID, ret=oo;
	if(L<=m) ret=min(ret, query2(lson, s, L, R));
	if(m<R) ret=min(ret, query2(rson, s, L, R));
	return ret;
}
int query1(int l, int r, int x, int L, int R, int LL, int RR) {
	if(L<=l && r<=R) return query2(1, mm, 1, mn[x], LL, RR);
	int m=MID, ret=oo;
	if(L<=m) ret=min(ret, query1(lson, L, R, LL ,RR));
	if(m<R) ret=min(ret, query1(rson, L, R, LL, RR));
	return ret;
}
ll ask(int x, int y) {
	int ret=query1(1, nn, 1, x, x+A-1, y, y+B-1); //printf("%d %d %d\n", x, y, ret);
	ll s=sum[x][y]-sum[x+A][y]-sum[x][y+B]+sum[x+A][y+B];
	return s-(ll)ret*(ll)A*(ll)B;
}
void dfs(int x, int y, int a, int b) {
	if(vis[x][y]) return;
	vis[x][y]=1;
	if(a<A) dfs(x+1, y, a+1, b);
	if(b<B) dfs(x, y+1, a, b+1);
}
void init() {
	CC(mn, 0x7f);
	read(nn); read(mm); read(A); read(B);
	for1(i, 1, nn) for1(j, 1, mm) read(a[i][j]);
	//for1(i, 1, nn) { for1(j, 1, mm) printf("%d\t", a[i][j]); puts(""); }
	for3(i, nn, 1) for3(j, mm, 1) sum[i][j]=sum[i+1][j]+sum[i][j+1]-sum[i+1][j+1]+a[i][j];
	build1(1, nn, 1);
	for1(i, 1, nn-A+1) for1(j, 1, mm-B+1) {
		home[++cnt].x=i;
		home[cnt].y=j;
		home[cnt].d=ask(i, j);
	}
}
int main() {
	init();
	sort(home+1, home+1+cnt, cmp);
	for1(i, 1, cnt) {
		int x=home[i].x, y=home[i].y;
		if(!vis[x][y] && !vis[x+A-1][y] && !vis[x][y+B-1] && !vis[x+A-1][y+B-1]) {
			ans[++ct]=home[i];
			dfs(x, y, 1, 1);
		}
	}
	printf("%d\n", ct);
	for1(i, 1, ct) printf("%d %d %lld\n", ans[i].x, ans[i].y, ans[i].d);
	return 0;
}

  


背景

安得广厦千万间,天下寒士俱欢颜。

描述

小D终于成为了一位建筑师。他的第一个任务是在一块n行m列的矩形土地上砌房子,每个房子的大小是a行b列(不能旋转)。

矩形土地的每个单元格都有一个高度。如果选定某个区域上为建房子的地方,那么需要将这个区域的每个单元格的高度变成这个区域的最小的单元格的高度,因为这样能使土地更平整。将一个单元格的高度从h2变为h1所花费的代价是h2-h1,一个区域所花费的代价为其每个单元格所花费的代价之和。

现在小D按下面所述的方式建房子:
1、首先找到矩形土地中花费最少代价就能建房子的区域(这个区域中不能有某个单元格已经砌了房子),如果有多个这样的区域,选择左上角所在行尽可能小的,如果行相同,选择列尽可能小的。

2、接下来在这块区域砌一栋房子。

3、重复上述操作,直到找不到一个可以砌房子的区域。

现在需要你告诉小D,他一共将砌多少栋房子,每栋房子的左上角的坐标,以及每栋房子所花费的代价。

格式

输入格式

输入的第一行包括四个正整数
n,m,a,b(含义如上所述)
接下来n行,每行m个非负整数
其中第i行,第j列表示第i行,第j列的单元格的高度。

输出格式

输出第一行为一个非负整数S:表示一共砌了多少栋房子
接下来S行
每行三个整数i,j,v:表示第S个房子的左上角的所在行,所在列,以及所花费的代价。

样例1

样例输入1[复制]

3 2 1 2
2 5
1 2
3 5

样例输出1[复制]

3
2 1 1
3 1 2
1 1 3

样例2

样例输入2[复制]

5 5 2 2
9 9 4 4 10
10 3 3 9 3
3 6 3 8 1
4 2 10 9 8
7 10 10 3 5 

样例输出2[复制]

4
2 2 3
4 4 13
1 4 14
4 1 15

限制

每个测试点2s

提示

30%保证:n <= 10, m <= 10
70%保证:n <= 300, m <= 300
100%保证:n <= 1000, m <= 1000
1<= a <= n, 1 <= b <= m, 每个单元格的高度 <= 10^9

来源

codeforces

时间: 2024-12-20 08:15:33

【vijos】1750 建房子(线段树套线段树+前缀和)的相关文章

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常young 的她写了如下的算法: 1: function Add(x

hdu-4819-线段树套线段树

http://acm.hdu.edu.cn/showproblem.php?pid=4819 给出一个N*N的矩阵,每次询问一个m*m的子矩阵里的floor((maxv+minv)/2)并把中间的元素修改为这个值. 线段树套线段树,第一层X表示对行建立的线段树,内层表示对Y也就是列建立的线段树. 分别对X和Y建立相应的函数来完成操作,当更新X树的节点时,再更新完当前X的节点的左右儿子之后,回头对X的这个节点对应的Y树进行更新,相当于X的左右儿子里的Y树来更新X的Y树,能理解这一点就很简单了. 1

ZJOI 2017 树状数组(线段树套线段树)

题意 http://uoj.ac/problem/291 思路 不难发现,九条カレン醬所写的树状数组,在查询区间 \([1,r]\) 的时候,其实在查询后缀 \([r,n]\) :在查询 \([l,r](l\neq1)\) 的时候,则是在查询 \([l-1,r-1]\) .那么在查询 \([1,r]\) 的时候,只需要询问 \(r\) 的前后缀异或是否相等:在查询 \([l,r](l\neq 1)\) 的时候,只需要询问 \(a[l-1],a[r]\) 是否相等. 考虑 \(O(n^2)\) 的

HDU - 5390 tree 线段树套字典树 (看题解)

HDU - 5390 看到的第一感觉就是树链剖分 + 线段树套字典树, 感觉复杂度不太对. 其实这种路径其实很特殊, 一个点改变只会影响它儿子到根的路径, 并且这种求最优值问题可以叠加. 所以我们修改的时候对对应dfs序打标记, 询问的时候在线段树上从上往下对每个对应区间求个最优值. 这样还会被卡MLE.. 需要分层优化一下. #pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> #define LL l

BZOJ 3217 ALOEXT 替罪羊树套Trie树

题目大意:维护一个序列,支持以下操作: 1.在某个位置插入一个数 2.删除某个位置上的数 3.修改某个位置上的数 4.求某段区间中的次大值与区间中另一个数的异或值的最大值 强制在线 替罪羊树套Trie树...终于尼玛A了...7.4KB的大代码啊- - 插入和修改同带插入区间k小值 删除要打标记不能直接删 删除的时候注意 删除导致的不平衡不要重建 否则复杂度无法保证 因此每个节点维护一个max_size代表历史size最大值 判断不平衡时用这个变量来判断即可 注意访问替罪羊树的时候一定要判断当前

【bzoj3217】ALOEXT 替罪羊树套Trie树

题目描述 taorunz平时最喜欢的东西就是可移动存储器了……只要看到别人的可移动存储器,他总是用尽一切办法把它里面的东西弄到手. 突然有一天,taorunz来到了一个密室,里面放着一排可移动存储器,存储器里有非常珍贵的OI资料……不过比较特殊的是,每个存储器上都写着一个非负整数.taorunz很高兴,要把所有的存储器都拿走(taorunz的智商高达500,他一旦弄走了这里的所有存储器,在不久到来的AHOI和NOI中……你懂的).不过这时有一个声音传来:“你只能拿走这里的一个存储器,而且还不能直

POJ 2155 树套树—线段树套线段树

Matrix 楼教主出的题目. 题意:一个矩阵初始值都为0,每次给"C X1 Y1 X2 Y2" 去反转这个矩阵.或者"Q X1 Y1"查询这个点是0/1. 第一次接触树套树的题目. 一句AC:对于基本的线段树,再在每个节点建一个y方向上的线段树.tree[n][m] 这道题目更新的时候,对于X方向就是(X1,X2)这个区间,再在其上对Y1,Y2进行更新. 对于查询,X方向上,自顶向下到X1都要对Y进行查询(更新的区间必包括该点),Y方向上则更新到Y1. #incl

BZOJ 3065 带插入区间K小值 替罪羊树套线段树

题目大意:带插入,单点修改的区间k小值在线查询. 思路:本年度做过最酸爽的题. 树套树的本质是一个外层不会动的树来套一个内层会动(或不会动)的树.两个树的时间复杂度相乘也就是差不多O(nlog^2n)左右.但是众所周知,高级数据结构经常会伴有庞大的常数,所以一般来说树套树的常数也不会小到哪去.所以在做这种题的时候先不要考虑常数的问题... 为什么要用替罪羊树呢?因为一般的平衡树都是会动的,这就很难办了.外层的树动了之后,内层的树肯定也是会动的.很显然,一般的二叉平衡树会经常会旋转,这样在动外层的