首页 > 代码库 > 【vijos】1750 建房子(线段树套线段树+前缀和)

【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 << endlinline 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)>>1typedef 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 22 51 23 5

样例输出1[复制]

 
32 1 13 1 21 1 3

样例2

样例输入2[复制]

 
5 5 2 29 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[复制]

 
42 2 34 4 131 4 144 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

 

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