• 「Gym102956F」Border Similarity Undertaking


    题目

    点这里看题目。

    分析

    对于任何一个合法的矩形 ((x_1,y_1,x_2,y_2))([x_1,x_2])([y_1,y_2]) 分别是行和列上的一个区间。由于合法的矩形还没啥比较好的性质,我们可以对于矩阵进行分治,每次对于行和列中较长者进行切分,并且计算某一维跨过了划分点的合法矩形的个数。

    不妨假设我们对于列划分,由于我们计算的是跨过中心列 (mi) 的矩形,所以中心列左侧需要求的是“匚”型,右侧恰好对称。于是我们只考虑左侧的统计方法。先暴力一点,我们可以枚举“匚”的上边界、下边界分别为第 (u) 行和第 (v) 行。预处理 (mathrm{le/ri/up/dn}[i][j]) 分别表示 ((i,j)) 向左、右、上、下四个方向,最远同色块的列号/行号。因此对于第 (u) 行和第 (v) 行,我们需要的结果为:

    [sum_{i=max{mathrm{le}[u][mi],mathrm{le}[v][mi]}}^{mi}[mathrm{dn}[u][i]ge v] ]

    由于 (i) 的下界要么为 (mathrm{le}[u][mi]),要么是 (mathrm{le}[v][mi]),我们可以对这两种情况分别预处理出结果,实际询问的时候再套用 (mathrm{le}) 较大的一个的答案。考虑固定 (u),我们假设 (mathrm{le}[u][mi]) 成为较大值,这样我们就可以枚举 (i)。此时 ((u,i)) 会对于 (i<vle mathrm{dn}[u][i])(v) 造成贡献,直接差分即可。相应地,我们也可以固定 (v),枚举 (i)。此时则是由 ((v,i)) 对于 (mathrm{up}[v][i]le u<i)(u) 造成贡献,同样差分。

    最终计算答案的时候,我们枚举 (u,v),将左侧上边界为第 (u) 行,下边界为第 (v) 行的“匚”的数量和右侧对称形的数量相乘即可,注意判断能否拼起来。

    考虑这样做的复杂度。对于一个 (p imes q) 的矩形分治的时候,单层复杂度为 (O(min{p,q}(p+q))le O(pq)),因此可以保证分治过程中每一层的复杂度为 (O(nm)),有 (O(log n)) 层,复杂度即为 (O(nmlog n))

    小结:

    1. 此处由于行、列均可以被表示到区间上,我们可以分治;由于行、列对称,我们可以选择较长的一维划分,保证复杂度。注意一下利用 (min) 来优化复杂度的方式,通常这意味着对称性。
    2. 统计过程中优化复杂度的方法是对于每种可能的情况都算一遍,来规避枚举到了 (u,v) 才知道 (i) 的边界的情况。这适用于实际情况较少,可以暴力处理每种情况的时候,类似的有 「NOI2021」量子通信中规避“在线”的方法。

    代码

    #include <cstdio>
    #include <iostream>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const int MAXN = 2005;
    
    template<typename _T>
    void read( _T &x )/*{{{*/
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    	x *= f;
    }/*}}}*/
    
    template<typename _T>
    void write( _T x )/*{{{*/
    {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }/*}}}*/
    
    int thkL[MAXN][MAXN], thkR[MAXN][MAXN];
    
    int up[MAXN][MAXN], dn[MAXN][MAXN];
    int lef[MAXN][MAXN], rig[MAXN][MAXN];
    char grid[MAXN][MAXN];
    
    LL ans = 0;
    int N, M;
    
    void Divide( const int xL, const int xR, const int yL, const int yR )/*{{{*/
    {
    	if( xL == xR || yL == yR ) return ;
    	if( xR - xL + 1 <= yR - yL + 1 )
    	{/*{{{*/
    		int mid = ( yR + yL ) >> 1, L, R;
    		rep( i, xL, xR ) rep( j, xL, xR ) thkL[i][j] = thkR[i][j] = 0;
    		rep( i, xL, xR )/*{{{*/
    		{
    			for( int j = mid ; j >= yL && j >= lef[i][mid] ; j -- )
    				thkL[i][std :: min( dn[i][j], xR )] ++,
    				thkL[i][std :: max( up[i][j], xL )] ++;
    			per( j, xR - 1, i ) thkL[i][j] += thkL[i][j + 1];
    			rep( j, xL + 1, i ) thkL[i][j] += thkL[i][j - 1];
    			for( int j = mid + 1 ; j <= yR && j <= rig[i][mid + 1] ; j ++ )
    				thkR[i][std :: min( dn[i][j], xR )] ++,
    				thkR[i][std :: max( up[i][j], xL )] ++;
    			per( j, xR - 1, i ) thkR[i][j] += thkR[i][j + 1];
    			rep( j, xL + 1, i ) thkR[i][j] += thkR[i][j - 1];
    		}/*}}}*/
    		rep( i, xL, xR ) rep( j, i + 1, xR )/*{{{*/
    		{
    			if( grid[i][mid] ^ grid[j][mid] ) continue;
    			if( grid[i][mid] ^ grid[i][mid + 1] ) continue;
    			if( grid[i][mid] ^ grid[j][mid + 1] ) continue;
    			L = lef[i][mid] >= lef[j][mid] ? thkL[i][j] : thkL[j][i];
    			R = rig[i][mid + 1] <= rig[j][mid + 1] ? thkR[i][j] : thkR[j][i];
    			ans += 1ll * L * R;
    		}/*}}}*/
    		Divide( xL, xR, yL, mid );
    		Divide( xL, xR, mid + 1, yR );
    	}/*}}}*/
    	else
    	{/*{{{*/
    		int mid = ( xR + xL ) >> 1, L, R;
    		rep( i, yL, yR ) rep( j, yL, yR ) thkL[i][j] = thkR[i][j] = 0;
    		rep( i, yL, yR )/*{{{*/
    		{
    			for( int j = mid ; j >= xL && j >= up[mid][i] ; j -- )
    				thkL[i][std :: min( rig[j][i], yR )] ++,
    				thkL[i][std :: max( lef[j][i], yL )] ++;
    			per( j, yR - 1, i ) thkL[i][j] += thkL[i][j + 1];
    			rep( j, yL + 1, i ) thkL[i][j] += thkL[i][j - 1];
    			for( int j = mid + 1 ; j <= xR && j <= dn[mid + 1][i] ; j ++ )
    				thkR[i][std :: min( rig[j][i], yR )] ++,
    				thkR[i][std :: max( lef[j][i], yL )] ++;
    			per( j, yR - 1, i ) thkR[i][j] += thkR[i][j + 1];
    			rep( j, yL + 1, i ) thkR[i][j] += thkR[i][j - 1];
    		}/*}}}*/
    		rep( i, yL, yR ) rep( j, i + 1, yR )/*{{{*/
    		{
    			if( grid[mid][i] ^ grid[mid][j] ) continue;
    			if( grid[mid][i] ^ grid[mid + 1][i] ) continue;
    			if( grid[mid][i] ^ grid[mid + 1][j] ) continue;
    			L = up[mid][i] >= up[mid][j] ? thkL[i][j] : thkL[j][i];
    			R = dn[mid][i] <= dn[mid][j] ? thkR[i][j] : thkR[j][i];
    			ans += 1ll * L * R;
    		}/*}}}*/
    		Divide( xL, mid, yL, yR );
    		Divide( mid + 1, xR, yL, yR );
    	}/*}}}*/
    }/*}}}*/
    
    int main()
    {
    	read( N ), read( M );
    	rep( i, 1, N ) scanf( "%s", grid[i] + 1 );
    	rep( i, 1, N ) rep( j, 1, M ) 
    	{
    		up[i][j] = i > 1 && grid[i][j] == grid[i - 1][j] ? up[i - 1][j] : i;
    		lef[i][j] = j > 1 && grid[i][j] == grid[i][j - 1] ? lef[i][j - 1] : j;
    	}
    	per( i, N, 1 ) per( j, M, 1 )
    	{
    		dn[i][j] = i < N && grid[i][j] == grid[i + 1][j] ? dn[i + 1][j] : i;
    		rig[i][j] = j < M && grid[i][j] == grid[i][j + 1] ? rig[i][j + 1] : j;
    	}
    	Divide( 1, N, 1, M );
    	write( ans ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    保存windows 10的登录界面壁纸
    Python 从剪贴板中生成二维码
    SpringBoot
    IDEA8条配置
    Nodejs-hexoBlog
    Mybatis-Plus
    Javaweb文件上传
    GIt基本语法
    JS常用部分整合
    javaweb-maven学习总结
  • 原文地址:https://www.cnblogs.com/crashed/p/15182878.html
Copyright © 2020-2023  润新知