• CCPC2020 秦皇岛站 B Bounding Wall


    传送门


    这题训练赛的时候想都没怎么想,感觉就是一个大数据结构题。实际上,这题虽然不难,但是对于我来说,考场上至少需要一个半点儿才能写出来。


    注意到(n,m,Q leqslant 1000),因此单次的询问可以是(O(nlog n))的。

    不妨令矩形的下边界经过((x,y)),只枚举以这条边为底的矩形。那么只要将整个矩阵旋转4次,就能涵盖所有情况了。

    那么对于这条底边,我们(O(n))枚举矩形的上边界。那么首先需要确定上下底边:我们能够很简单的(O(n^2))预处理出每个点向左和向右扩展的最大距离,这样上下底边左右扩展出来的距离取(min),就能得到上下底边的范围了。

    接下来只要确定竖着的边了。会发现左右部分互不影响,因此可以单独考虑。

    以左半部分为例。记下底边所在的行为(i),上底边所在的行为(j),底边向左扩展到的最远的一列是(x)。如果我们预处理出每个点向上扩展出的最大距离(up[i][j]),那么我们要找的就是最小的(p),满足(p geqslant x)(up[i][p] geqslant i - j +1).而这个,可以用线段树在(O(log n))时间内实现。(题解中用的是树状数组或并查集,但是我没想明白)

    综上所述,对于每次查询((x,y)),先用(x)这一行的(up[x][i])建一棵线段树,然后枚举上底边,并用线段树查询满足条件的最小/最大的(p).一次查询的总时间复杂度是(O(nlog m))的。

    而对于修改,一个点影响到的点只有以他为中心的一个十字,暴力更改即可,这个单次修改的时间复杂度是(O(n+m))的。

    还有一点就是,因为旋转整个矩阵的时间复杂度是(O(nm)),因此需要离线操作,这样总的复杂度才能保证是(O(4qnlog m+4n*m)).

    #include<bits/stdc++.h>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 1e3 + 5;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    int n, m, Q, a[maxn][maxn], ans[maxn];
    char s[maxn][maxn];
    struct Query {int op, x, y;}q[maxn];
    
    int up[maxn][maxn], lft[maxn][maxn], rgt[maxn][maxn];
    
    int l[maxn << 2], r[maxn << 2];
    int Max[maxn << 2];
    In void build(int L, int R, int now, int id)
    {
    	l[now] = L, r[now] = R, Max[now] = 0;
    	if(L == R) {Max[now] = up[id][L]; return;}
    	int mid = (l[now] + r[now]) >> 1;
    	build(L, mid, now << 1, id), build(mid + 1, R, now << 1 | 1, id);
    	Max[now] = max(Max[now << 1], Max[now << 1 | 1]);
    }
    In int queryL(int L, int R, int now, int h)		//查找满足条件且最靠左的点
    {
    	if(Max[now] < h) return 0;
    	if(l[now] == r[now]) return l[now];
    	int mid = (l[now] + r[now]) >> 1;
    	if(R <= mid) return queryL(L, R, now << 1, h);
    	else if(L > mid) return queryL(L, R, now << 1 | 1, h);
    	else
    	{
    		int ret = queryL(L, mid, now << 1, h);
    		if(!ret) ret = queryL(mid + 1, R, now << 1 | 1, h);
    		return ret;
    	}
    }
    In int queryR(int L, int R, int now, int h)		//查找满足条件且最靠右的点
    {
    	if(Max[now] < h) return 0;
    	if(l[now] == r[now]) return l[now];
    	int mid = (l[now] + r[now]) >> 1;
    	if(R <= mid) return queryR(L, R, now << 1, h);
    	else if(L > mid) return queryR(L, R, now << 1 | 1, h);
    	else
    	{
    		int ret = queryR(mid + 1, R, now << 1 | 1, h);
    		if(!ret) ret = queryR(L, mid, now << 1, h);
    		return ret;
    	}
    }
    In void Change(int x, int y)
    {
    	a[x][y] ^= 1;
    	for(int j = y; j; --j) rgt[x][j] = a[x][j] ? rgt[x][j + 1] + 1 : 0;
    	for(int j = y; j <= m; ++j) lft[x][j] = a[x][j] ? lft[x][j - 1] + 1 : 0;
    	for(int i = x; i <= n; ++i) up[i][y] = a[i][y] ? up[i - 1][y] + 1 : 0;
    }
    In int Query(int x, int y)
    {
    	if(!a[x][y]) return 0;
    	int l = lft[x][y], r = rgt[x][y], ans = 0;
    	build(1, m, 1, x);
    	for(int i = x; i; --i)
    	{
    		int nl = min(l, lft[i][y]), nr = min(r, rgt[i][y]);
    		if(!nl || !nr) continue;
    		int ql = queryL(y - nl + 1, y, 1, x - i + 1), qr = queryR(y, y + nr - 1, 1, x - i + 1);
    		if(ql && ql <= y && y <= qr) ans = max(ans, (qr - ql + 1) * (x - i + 1));
    	}
    	return ans;
    }
    In void solve()
    {
    	for(int i = 0; i <= n + 4; ++i)
    		for(int j = 0; j <= m + 4; ++j) up[i][j] = lft[i][j] = rgt[i][j] = 0;
    	for(int i = 1; i <= n; ++i)		//向上延伸的最长距离 
    		for(int j = 1; j <= m; ++j)
    			up[i][j] = (a[i][j] ? up[i - 1][j] + 1 : 0);
    	for(int j = 1; j <= m; ++j)		//向左延伸 
    		for(int i = 1; i <= n; ++i)
    			lft[i][j] = (a[i][j] ? lft[i][j - 1] + 1 : 0);
    	for(int j = m; j; --j)			//向右延伸 
    		for(int i = 1; i <= n; ++i)
    			rgt[i][j] = (a[i][j] ? rgt[i][j + 1] + 1 : 0);
    	for(int i = 1; i <= Q; ++i)
    	{
    		if(q[i].op == 1) Change(q[i].x, q[i].y);
    		else ans[i] = max(ans[i], Query(q[i].x, q[i].y));
    	}
    }
    char s1[maxn][maxn]; 
    In void rotate()
    {
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= m; ++j) s1[m - j + 1][i] = s[i][j];
    	for(int i = 1; i <= Q; ++i)
    	{
    		int nx = m - q[i].y + 1, ny = q[i].x;
    		q[i].x = nx, q[i].y = ny;
    	}	
    	swap(n, m);
    	for(int i = 1; i <= n; ++i)
    		for(int j = 1; j <= m; ++j) s[i][j] = s1[i][j], a[i][j] = (s[i][j] == '#');
    
    }
    
    int main()
    {
    	int T = read(), ID = 0;
    	while(T--)
    	{
    		n = read(), m = read(), Q = read();
    		for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
    		for(int i = 1; i <= Q; ++i) q[i].op = read(), q[i].x = read(), q[i].y = read();
    		Mem(ans, 0);
    		for(int i = 1; i <= 4; ++i) rotate(), solve();
    		printf("Case #%d:
    ", ++ID);
    		for(int i = 1; i <= Q; ++i) if(q[i].op == 2) write(ans[i]), enter;
    	}
    	return 0;
    }
    
  • 相关阅读:
    NHibernate Session-per-request and MiniProfiler.NHibernate
    NHibernate ConfORM Mapping
    微信小程序之网页开发——博客园老牛大讲堂
    帝国后台CMS模版使用
    微信小程序开发---博客园老牛大讲堂
    帝国CMS浅浅滴谈一下——博客园老牛大讲堂
    Java实现 第三方的验证码发送问题--博客园老牛大讲堂
    关于websocket自定义通信,聊天工具--博客园老牛大讲堂
    对ECMAScript的理解---博客园老牛大讲堂
    Css3引用外部字体样式---博客园老牛大讲堂
  • 原文地址:https://www.cnblogs.com/mrclr/p/15473461.html
Copyright © 2020-2023  润新知