[GXOI/GZOI2019]与或和

考虑拆位,计算每一个二进制位的贡献。

问题转化为求一个01矩阵的全0/1的子矩形个数。

考虑计算以第i行第j列为右下角的合法子矩形个数。

发现合法的左上角范围向左是单调下降的。

可以用一个单调栈来维护合法的范围。

这样做总复杂度O(n^2logn)

#include<bits/stdc++.h>
#define N 2200
#define eps 1e-7
#define inf 1e9+7
#define db double
#define ll long long
#define ldb long double
using namespace std;
inline int read()
{
	char ch=0;
	int x=0,flag=1;
	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*flag;
}
const int mo=1e9+7;
int n,f[N],w[N],st[N],s[N][N];
int work1()
{
	int res=0;
	for(int o=0;o<=31;o++)
	{
		int ans=0;
		for(int i=1;i<=n;i++)f[i]=0;
		for(int x=1;x<=n;x++)
		{
			for(int i=1;i<=n;i++)f[i]=((1<<o)&s[x][i])?f[i]+1:0;
			for(int i=1,top=0;i<=n;i++)
			{
				while(top&&f[st[top]]>f[i])top--;
				st[++top]=i;w[top]=(w[top-1]+(1ll*(st[top]-st[top-1])*f[i]%mo))%mo;
				ans=(ans+w[top])%mo;
			}
		}
		res=(res+(1ll*(1<<o)*ans%mo))%mo;
	}
	return (res%mo+mo)%mo;
}
int work2()
{
	int res=0;
	for(int o=0;o<=31;o++)
	{
		int ans=0;
		for(int i=1;i<=n;i++)f[i]=0;
		for(int x=1;x<=n;x++)
		{
			for(int i=1;i<=n;i++)f[i]=(!((1<<o)&s[x][i]))?f[i]+1:0;
			for(int i=1,top=0;i<=n;i++)
			{
				while(top&&f[st[top]]>f[i])top--;
				st[++top]=i;w[top]=(w[top-1]+(1ll*(st[top]-st[top-1])*f[i]%mo))%mo;
				ans=(ans+w[top])%mo;
			}
		}
		ans=((1ll*(n*(n+1)/2)*(n*(n+1)/2)%mo)-ans)%mo;
		res=(res+(1ll*(1<<o)*ans%mo))%mo;
	}
	return (res%mo+mo)%mo;
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)s[i][j]=read();
	printf("%d %d",work1(),work2());
	return 0;	
}
posted @ 2019-04-26 08:05  Creed-qwq  阅读(174)  评论(0编辑  收藏  举报