【洛谷P5300】與或和


題目

題目鏈接:https://www.luogu.com.cn/problem/P5300
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)\) 取模后的結果。

思路

樣例一提示我們在一個01矩陣內先求答案。
我們發現,在01矩陣內,若子矩陣\(and\)起來為1,那么需要滿足這個矩陣全部數字都為1。如果需要\(or\)起來為1,則需要滿足這個矩陣內至少有一個位置為1,那么就可以用總矩陣個數\(-\)全為0的矩陣個數。
也就是說,在01矩陣內,我們只要求出有多少個全為1的子矩陣和有多少個全為0的子矩陣,那么\(and\)值之和與\(or\)值之和都可求。
顯然這個是可以用單調棧在\(O(n^2)\)時間內維護的。
那么考慮原題。我們可以將其拆分成\(log(max\{a\})\)個01矩陣來做,第\(i\)位的子矩陣個數乘上\(2^i\)即可。
時間復雜度\(O(n^2\log n)\)

代碼

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <stack>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;

const int N=1010,MOD=1e9+7,LG=30;
int n,map[N][N],a[N][N];
ll ans1,ans2,sum;

inline int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

inline ll solve(int p,int id)
{
	ll cnt=0;
	for (register int i=1;i<=n;i++)
		for (register int j=1;j<=n;j++)
			if (((map[i][j]&(1<<p))>0)!=id) a[i][j]=0;
				else a[i][j]=a[i-1][j]+1;
	for (register int i=1;i<=n;i++)
	{
		stack<pair<int,int> > s;
		s.push(mp(0,0));
		for (register int j=1;j<=n+1;j++)
		{
			int last=j,high;
			while (s.size()>1 && s.top().nd>a[i][j])
			{
				last=s.top().st; high=s.top().nd;
				s.pop();
				cnt=(cnt+(high-max(s.top().nd,a[i][j]))*(1+j-last)*(j-last)/2LL)%MOD;
			}
			s.push(mp(last,a[i][j]));
		}
	}
	return cnt;
}

int main()
{
	n=read();
	for (register int i=1;i<=n;i++)
		for (register int j=1;j<=n;j++)
		{
			map[i][j]=read();
			sum=(sum+i*j)%MOD;
		}
	for (register int i=0;i<=LG;i++)
	{
		ans1=(ans1+(1LL<<i)*solve(i,1))%MOD;
		ans2=(ans2+(1LL<<i)*(sum-solve(i,0)))%MOD;
	}
	printf("%lld %lld",ans1,ans2);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM