LG5300 「GZOI2019/GXOI2019」与或和 二进制+单调栈

问题描述

Freda 学习了位运算和矩阵以后,决定对这种简洁而优美的运算,以及蕴含深邃空间的结构进行更加深入的研究。

对于一个由非负整数构成的矩阵,她定义矩阵的 \(\texttt{AND}\) 值为矩阵中所有数二进制 \(\texttt{AND(&)}\) 的运算结果;定义矩阵的 \(\texttt{OR}\) 值为矩阵中所有数二进制 \(\texttt{OR(|)}\) 的运算结果。

给定一个 \(N \times N\) 的矩阵,她希望求出:

  1. 该矩阵的所有子矩阵的 \(\texttt{AND}\) 值之和(所有子矩阵 \(\texttt{AND}\) 值相加的结果)。
  2. 该矩阵的所有子矩阵的 \(\texttt{OR}\) 值之和(所有子矩阵 \(\texttt{OR}\) 值相加的结果)。

接下来的剧情你应该已经猜到——Freda 并不想花费时间解决如此简单的问题,所以这个问题就交给你了。

由于答案可能非常的大,你只需要输出答案对 \(1,000,000,007 (10^9 + 7)\) 取模后的结果。

LG5300


题解

发现 and 和 or 大毒瘤出现开幕雷击,于是考虑按位计算贡献。

问题就简化为了01矩阵。

于是一个子矩阵全是0的话对or有负贡献,全是1的话对and有正贡献。

单调栈维护即可


\(\mathrm{Code}\)

#include<bits/stdc++.h>
using namespace std;

#define int long long

template <typename Tp>
void read(Tp &x){
	x=0;char ch=1;int fh=1;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') ch=getchar(),fh=-1;
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	x*=fh;
}

const int mod=1000000007;
const int maxn=1007;

int a[maxn][maxn],n;
long long ansor,ansand;
int s[maxn],h[maxn],val[maxn],pos;
int mx;

void Init(void){
	read(n);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
		read(a[i][j]),mx=max(mx,a[i][j]);
}

long long sqr(long long x){
	return (long long)x*(x+1)/2;
}

int calc(int need){
	long long res=0;
	memset(h,0,sizeof(h));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if((a[i][j]&1)==need) h[j]++;
			else h[j]=0;
			if(h[j]>s[pos]) s[++pos]=h[j],val[pos]=1;
			else{
				int k=0;
				while(h[j]<s[pos]){
					k+=val[pos];
					res=(res+(s[pos]-max(s[pos-1],h[j]))*sqr(k)%mod)%mod;
					--pos;
				}
				s[++pos]=h[j],val[pos]=k+1;
			}
		}
		int k=0;
		while(pos){
			k+=val[pos];
			res=(res+(s[pos]-s[pos-1])*sqr(k)%mod)%mod;
			--pos;
		}
	}
	return res;
}

void Work(void){
	int tp=0;
	while(mx){
		ansand=(ansand+calc(1)*(1<<tp)%mod)%mod;
		ansor=(ansor+(sqr(n)*sqr(n)%mod-calc(0))*(1<<tp)%mod)%mod;
		mx>>=1;tp++;
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]>>=1;
	}
	ansand=(ansand%mod+mod)%mod;
	ansor=(ansor%mod+mod)%mod;
	printf("%lld %lld\n",ansand,ansor);
}

signed main(){
//	freopen("andorsum.in","r",stdin);freopen("andorsum.out","w",stdout);
	Init();
	Work();
}
posted @ 2020-01-11 22:44  览遍千秋  阅读(171)  评论(1编辑  收藏  举报