GZOI2019 与或和

单调栈板子题..?

首先按位拆分,然后转变为统计一个01矩阵中所有全1的子矩阵(按位与)以及所有全0的子矩阵(用总矩阵数减掉即为按位或的答案)。

打表可知$n\times n$的矩阵有$(\frac{n(n+1)}2)^2$个子矩阵。

统计全1矩阵和全0矩阵本质一样,故我们先考虑统计全0矩阵。

考虑处理出来$f(i,j)$,表示如果第i行第j列的元素时0,那么往上到什么位置的元素也全都是0。对于是1的位置,$f(i,j)=-1$。

然后我们枚举一个下界$low$,表示现在我们要统计下边缘在$low$上的矩形个数。

然后从左到右扫,维护一个单调栈,单调栈内的元素是列,设$S_i$为栈中自底向上第$i$个元素,那么满足$f(low,i+1)>f(low,i)$。

先不考虑出栈,考虑入栈。

处理右下角为$(i,j)$时的答案时,设$x$表示合法的矩形数量

假设第i列的元素$i$入栈的时候,我们设$S_{-1}$表示栈内最后一个元素,$S_{-2}$以此类推。

如果加入元素i不会使得任何元素出栈,那么我们可以得到以$(i,j)$为右下角的所有合法的矩形数量为$x+f(low,i)$。

由于加入i不会导致元素出栈,故$f(low,i)$一定大于栈中其他元素的值,所以第i列较低的$S_{-1}$列可以和前面的拼起来,比$S_{-1}$高的部分可以自己成一个宽度为1的矩形。

如果导致了元素出栈呢?

左侧到$S_{-2}$,上边界到$f(low,S_{-1})$的矩形不可能再次被取到,减掉即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <inttypes.h>
#define debug(x) std::cout << #x << " = " << x << std::endl;
/*
°´Î»ÌÖÂÛ
ANDֵΪ1->¾ØÕóÈ«²¿Îª1
ORֵΪ1->¾ØÕóÖÁÉÙÓÐÒ»¸ö1
ͳ¼ÆÈ«0ºÍÈ«1µÄ¾ØÕó¸öÊý£¬ÖÁÉÙÓÐÒ»¸ö1µÈ¼ÛÓÚ×Ü×Ó¾ØÕóÊý-È«0¾ØÕó¡£
*/
typedef int int_t;

using std::cin;
using std::endl;
using std::cout;
const int_t mod = 1e9 + 7;
const int_t LARGE = 1e3+3;
int_t mat[LARGE+1][LARGE+1];

//last:ij¸öλÖÃÉÏ·½µÄµÚÒ»¸ö1µÄλÖÃ
int_t count[LARGE+1][LARGE+1],last[2][LARGE+1][LARGE+1];
int_t resultAnd,resultOr;
int_t n;
int_t C2(int_t n) {
	return ((int64_t)n*(n-1)/2)%mod;
}
void countFor(int_t bit) {
	//ö¾ÙÁÐ
	for(int_t i=1; i<=n; i++) {
		last[0][0][i]=last[1][0][i]=1;
		for(int_t j=1; j<=n; j++) {
			for(int_t k=0; k<=1; k++) {
				if(count[j-1][i]!=k) {
					last[k][j][i]=j;
				} else {
					last[k][j][i]=last[k][j-1][i];
				}
				if(count[j][i]!=k) last[k][j][i]=-1;
			}
		}
	}
	int_t zero=0,one=0;
	//ö¾Ùµ×
	for(int_t low=1; low<=n; low++) {
		static int_t val[2][LARGE+1];
		for(int_t i=1; i<=n; i++) {
			for(int_t j=0; j<=1; j++) {
				if(last[j][low][i]==-1) val[j][i]=0;
				else val[j][i]=low-last[j][low][i]+1;
			}
		}
		const auto count=[&](int_t bit) {
			int_t result=0;
			int_t count = 0;
			//´Ó×óÍùÓÒɨ
			std::vector<int_t> stack;
			stack.push_back(0);
			val[bit][0]=-1;
			for(int_t i=1; i<=n; i++) {
				//ºÍÇ°ÃæÁ¬µ½Ò»ÆðµÄ & ×Ô¼ºµÄ
				count += val[bit][i];
				while(stack.size() >= 1 && val[bit][stack.back()]>=val[bit][i]) {
					count -= (stack.back() - stack[stack.size() - 2]) * (val[bit][stack.back()] - val[bit][i]);
					stack.pop_back();
				}
				stack.push_back(i);
				result += count;
			}
			return result;
		};
		zero=(zero+count(0))%mod;
		one=(one+count(1))%mod;
	}
	resultAnd=(resultAnd+(int64_t)one*(1ll<<bit)%mod)%mod;
	const auto pow2=[](int64_t x) {
		x%=mod;
		return x*x%mod;
	};
	int_t least = (pow2((int64_t)n*(n+1)/2) - zero + mod) % mod;
	resultOr=((int64_t)resultOr+least*(1ll<<bit)%mod)%mod;
}
namespace SimpleIO{
    const int_t LARGE = 5e7;
    char buf[LARGE + 1];
    char* used = buf;
    void init(){
        fread(buf,1,LARGE,stdin);
    }
    template<class T>
    void nextInt(T& x){
        x = 0;
        while(isdigit(*used) == false) used++;
        while(isdigit(*used)){
            x = x * 10 + (*used) - '0';
            used++;
        }
    }
}
int main() {
//	scanf("%d",&n);
    SimpleIO::init();
    SimpleIO::nextInt(n);
	int_t maxval=0;
	for(int_t i=1; i<=n; i++) {
		for(int_t j=1; j<=n; j++) {
//			scanf("%d",&mat[i][j]);
            SimpleIO::nextInt(mat[i][j]);
			maxval=std::max(maxval,mat[i][j]);
		}
	}
	for(int_t i=0; (1ll<<i)<=maxval; i++) {
		for(int_t j=1; j<=n; j++) {
			for(int_t k=1; k<=n; k++) {
				count[j][k]=(mat[j][k]&(1ll<<i))!=0;
			}
		}
		countFor(i);
	}
	cout << resultAnd << " " << resultOr << endl;
	return 0;
}

 

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理