【codeforces】480E Parking Lot 线段树+DP

2024-09-05 14:38

本文主要是介绍【codeforces】480E Parking Lot 线段树+DP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

传送门:【codeforces】480E Parking Lot



题目分析:很早以前在同学助攻下写出来的,想想还是放出来好了,是个不错的思想。

原题是在动态将可行区域变成不可行区域的同时求全局最大子正方形,n,m,k至多2000。

既然只有加点,于是我们离线,倒着来,这样便只有删点。

我们给每个节点(i,j)保存它向上能延伸的距离U[i][j]以及向下能延伸的距离D[i][j]。

易知删除一个点最多影响一列上的U和D,暴力更新U,D。复杂度为O(n^2),可以承受。

首先我们预处理出所有点都加上后的最大子正方形(直接dp)。

然后建立一棵线段树,线段树保存一段区间向下延伸的最大子矩形的高度(即最小的D),同时保存一段区间向上延伸的最大子矩形的高度(即最小的U)。

每次删点时我们暴力维护U,D,然后对删点的那一行建立线段树进行最大子正方形大小的维护(最大子正方形大小的改变一定和这一行上的点有关)。

维护方法直接见代码好了。

然后查询就是O(1)的操作了。


觉得这解题的思想很不错,值得思考~


而且这题的解法貌似很多的样子。。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std ;typedef long long LL ;#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , mconst int MAXN = 2005 ;
const int INF = 0x3f3f3f3f ;struct Node {int x , y ;
} p[MAXN] ;int minu[MAXN << 2] , mind[MAXN << 2] ;
int G[MAXN][MAXN] ;
int U[MAXN][MAXN] ;
int D[MAXN][MAXN] ;
int left[MAXN] , right[MAXN] ;
int ans[MAXN] ;
int n , m , k ;int min1 , min2 ;
void query ( int L , int R , int o , int l , int r ) {if ( L <= l && r <= R ) {min1 = min ( min1 , minu[o] ) ;min2 = min ( min2 , mind[o] ) ;return ;}int m = mid ;if ( L <= m ) query ( L , R , lson ) ;if ( m <  R ) query ( L , R , rson ) ;
}void build ( int x , int o , int l , int r ) {if ( l == r ) {minu[o] = U[x][l] ;mind[o] = D[x][l] ;return ;}int m = mid ;build ( x , lson ) ;build ( x , rson ) ;minu[o] = min ( minu[ls] , minu[rs] ) ;mind[o] = min ( mind[ls] , mind[rs] ) ;
}int calc () {int ans = 0 ;For ( i , 1 , n ) {For ( j , 1 , m ) {int x = j ;while ( x > 1 && U[i][x - 1] >= U[i][j] ) x = left[x - 1] ;left[j] = x ;}rev ( j , m , 1 ) {int x = j ;while ( x < m && U[i][x + 1] >= U[i][j] ) x = right[x + 1] ;right[j] = x ;}For ( j , 1 , m ) {ans = max ( ans , min ( U[i][j] , right[j] - left[j] + 1 ) ) ;//printf ( "%d %d %d\n" , U[i][j] , left[j] , right[j] ) ;}//printf ( "%d\n" , ans ) ;}return ans ;
}int check ( int size , int y ) {if ( size > n || size > m ) return 0 ;For ( i , max ( 1 , y - size + 1 ) , m - size + 1 ) {min1 = min2 = INF ;query ( i , i + size - 1 , root ) ;if ( min1 + min2 - 1 >= size ) return 1 ;}return 0 ;
}void solve () {clr ( U , 0 ) ;clr ( D , 0 ) ;clr ( G , 0 ) ;clr ( ans , 0 ) ;For ( i , 1 , n ) {getchar () ;For ( j , 1 , m ) {char c = getchar () ;G[i][j] = c == '.' ;}}rep ( i , 0 , k ) {scanf ( "%d%d" , &p[i].x , &p[i].y ) ;G[p[i].x][p[i].y] = 0 ;}//For ( i , 1 , n ) For ( j , 1 , m ) printf ( "%d%c" , G[i][j] , j < m ? ' ' : '\n' ) ;For ( i , 1 , n ) For ( j , 1 , m ) U[i][j] = G[i][j] ? U[i - 1][j] + 1 : 0 ;rev ( i , n , 1 ) For ( j , 1 , m ) D[i][j] = G[i][j] ? D[i + 1][j] + 1 : 0 ;int size = calc () ;rev ( i , k - 1 , 0 ) {ans[i] = size ;if ( !i ) break ;int x = p[i].x ;int y = p[i].y ;G[x][y] = 1 ;U[x][y] = U[x - 1][y] + 1 ;For ( j , x + 1 , n ) {if ( !G[j][y] ) break ;U[j][y] += U[x][y] ;}D[x][y] = D[x + 1][y] + 1 ;rev ( j , x - 1 , 1 ) {if ( !G[j][y] ) break ;D[j][y] += D[x][y] ;}build ( x , root ) ;while ( check ( size + 1 , y ) ) ++ size ;}rep ( i , 0 , k ) printf ( "%d\n" , ans[i] ) ;
}int main () {while ( ~scanf ( "%d%d%d" , &n , &m , &k ) ) solve () ;return 0 ;
}


这篇关于【codeforces】480E Parking Lot 线段树+DP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139243

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]