习题:与或和(单调栈)

题目

传送门

思路

因为与位运算有关

所以第一点可以想到是算包含每一位的有哪些矩阵

有了这一个思路之后

问题可以分为两个子问题

问题1:如何求&

对于一个矩阵,如果我们假设这个矩阵的&值的二进制第k位为1

那么其中的每一个数的二进制的第k位都为1

有了这个之后,直接单调栈怼上去就行了

问题2:如何求|

如果采用和求&一样的方式去思考

我们发现如果这个矩阵的|值得二进制第k位为1

那么矩阵中至少有一个数的二进制第k位为1

但是我们发现这个东西不好直接求

但是你可以用全部的矩阵数量减去不合条件的矩阵

也就是说,

减去的不合条件的矩阵其实就是&值的二进制的第k位为0的矩阵

一样的单调栈怼上去就行了

代码

#include<iostream>
#include<cstdio>
using namespace std;
const int mod=1e9+7;
long long n;
long long s_top;
long long st[1005];
long long h[1005];
long long w[1005];
long long a[1005][1005];
long long ans_and;
long long ans_or;
long long solve_sum(long long limt)
{
	return limt*(limt+1)/2;
}
long long solve(int bj)
{
	long long tot=0;
	for(int i=1;i<=n;i++)
		h[i]=0;		
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if((a[i][j]&1)==bj)
				h[j]++;
			else
				h[j]=0;	
			if(h[j]>st[s_top])
			{
				st[++s_top]=h[j];
				w[s_top]=1;
			}
			else
			{
				long long wid=0;
				while(st[s_top]>h[j])
				{
					wid+=w[s_top];
					tot=(tot+(st[s_top]-max(st[s_top-1],h[j]))*solve_sum(wid))%mod;
					s_top--;	
				}
				st[++s_top]=h[j];
				w[s_top]=wid+1;
			}
		}
		long long wid=0;
		while(s_top)
		{
			wid+=w[s_top];
			tot=(tot+(st[s_top]-st[s_top-1])*solve_sum(wid))%mod;
			s_top--;
		}
	}
	return tot;
}
void dfs(int dep)
{
	if(dep==32)
		return;			
	ans_and=(ans_and+solve(1)*(1ll<<dep))%mod;
	ans_or=(ans_or+(solve_sum(n)*solve_sum(n)%mod-solve(0))*(1ll<<dep))%mod;
//cout<<solve(1)<<' '<<solve(0)<<'\n';
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]>>=1;
	dfs(dep+1);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j];
	dfs(0);
	cout<<(ans_and+mod)%mod<<' '<<(ans_or+mod)%mod;
	return 0;
}
posted @ 2019-12-14 13:58  loney_s  阅读(142)  评论(0)    收藏  举报