「GXOI / GZOI2019」与或和

题目

广西和贵州的省选?好像很神仙的样子啊

之后发现这是一道水题

我们显然应该拆位考虑

显然我们应该对于每一位都拆一下看看这一位是(0/1)

显然我们如果找到一个全是(1)的矩阵,那么这一位的(and)和不为(0),否则就是(0)

对于(or)和,我们只需要求出全是(0)的矩阵,之后拿总矩阵数量一减就是至少有一个(1)的矩阵的数量,这样的矩阵(or)和这一位显然是(1)

于是问题转化成了求有多少个全(0)(1)矩阵

我们预处理出每一个位置往右最多延伸的长度,对于一列来说我们需要求出所有子区间最小值的和

很好,我们发现这就是一个烜式合并单调栈的板子题了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=1e3+5;
const int mod=1e9+7;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int cnt,a[maxn][maxn],n,b[maxn][maxn],M;
int st[maxn],sz[maxn],l[maxn][maxn];
int top,sum;
inline int work(int w,int o) {
	for(re int i=1;i<=n;i++) 
		for(re int j=1;j<=n;j++) {
			if((a[i][j]&(1<<w))) b[i][j]=o;
				else b[i][j]=o^1;
		}
	for(re int i=1;i<=n;i++) {
		l[i][n]=b[i][n];
		for(re int j=n-1;j;--j)
		if(!b[i][j]) l[i][j]=0;
			else l[i][j]=l[i][j+1]+1;
	}
	int tmp=0;
	for(re int j=1;j<=n;j++) {
		top=0,sum=0;
		for(re int i=1;i<=n;i++) {
			int now=0;
			while(top&&st[top]>=l[i][j]) 
				sum-=st[top]*sz[top],now+=sz[top],top--;
			st[++top]=l[i][j];sz[top]=now+1;
			sum+=st[top]*sz[top];
			tmp=(tmp+sum)%mod;
		}
	}
	return tmp;
}
int main() {
	n=read();
	for(re int i=1;i<=n;i++) 
		for(re int j=1;j<=n;j++) 
			a[i][j]=read(),M=max(M,a[i][j]);
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=n;j++)
			cnt=(cnt+1ll*(n-i+1)*(n-j+1)%mod)%mod;
	int ans=0,tot=0;
	for(re int w=0;w<=32;w++) {
		if((1ll<<w)>M) break;
		int t=(1ll<<w)%mod;
		ans=(ans+1ll*t*work(w,1)%mod)%mod;
		tot=(tot+1ll*t*(cnt-work(w,0)+mod)%mod)%mod;
	}
	printf("%d %d
",ans,tot);
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/10712742.html