loj #3083. 「GXOI / GZOI2019」与或和

背景:

luogu \text{luogu} luogu ing.. \text{ing..} ing..
不知道为什么 loj \text{loj} loj上的 C++(NOI) \text{C++(NOI)} C++(NOI) C++ \text{C++} C++要慢,我还以为是人丑 常数大。

题目传送门:

https://loj.ac/problem/3083

题意:

给出一个矩阵,分别求所有子矩阵 and,or \text{and,or} and,or的和。

思路:

容易想到位与位之间不冲突,因此可以按位处理。
你考虑 and \text{and} and的情况,其实就是子矩阵全部为 1 1 1的数目;而 or \text{or} or其实就是总子矩阵数量 − - 全部为 0 0 0的子矩阵的数目。
怎么处理全部为 0 0 0 1 1 1的子矩阵的数目。
单调队列即可。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 1000000007
#define I inline
#define R register
#define LL long long
using namespace std;
	int n;
	int d[1010][1010];
	LL same[1010];
	struct node{LL x,y;} sta[1010];
	LL ans1=0,ans2=0;
I int read()
{
	int x=0,f=1;
	char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar())
		if(ch=='-') f=-1;
	for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
	return x*f;
}
I LL work(int x,int op)
{
	LL ans=0;
	memset(same,0,sizeof(same));
	for(R int i=1;i<=n;i++)
	{
		int top=0;
		LL sum=0;
		for(R int j=1;j<=n;j++)
		{
			LL row=1;
			same[j]=((d[i][j]>>x)&1)==op?same[j]+1:0;
			while(top&&same[j]<=sta[top].x)
			{
				sum=(sum-sta[top].x*sta[top].y%mod+mod)%mod;
				row=(row+sta[top].y)%mod;
				top--;
			}
			sum=(sum+same[j]*row%mod)%mod;
			ans=(ans+sum)%mod;
			sta[++top]=(node){same[j],row};
		}
	}
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	n=read();
	LL tot=((LL)(1+n)*n/2)*((LL)(1+n)*n/2)%mod;
	for(R int i=1;i<=n;i++)
		for(R int j=1;j<=n;j++)
			d[i][j]=read();
	for(R int i=0;i<=30;i++)
	{
		LL bin=(1<<i)%mod;
		ans1=(ans1+bin*work(i,1)%mod)%mod;
		ans2=(ans2+bin*((tot-work(i,0)+mod)%mod)%mod)%mod;
	}
	printf("%lld %lld",ans1,ans2);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值