D e s c r i p t i o n \mathcal{Description} Description
给出一个
n
×
n
n\times n
n×n 的, 元素为自然数的矩阵.
这个矩阵有许许多多个子矩阵, 定义它的所有子矩阵形成的集合为
S
S
S .
对于一个矩阵
k
k
k , 定义
f
(
k
)
f (k)
f(k) 为
k
k
k 中所有元素的
A
N
D
AND
AND 值 (按位与).
对于一个矩阵
k
k
k , 定义
g
(
k
)
g(k)
g(k) 为
k
k
k 中所有元素的
O
R
OR
OR 值 (按位或).
请求出所有子矩阵的
f
(
k
)
f (k)
f(k) 之和与所有子矩阵的
g
(
k
)
g(k)
g(k) 之和, 即
∑
k
∈
S
f
(
k
)
\begin{aligned}\sum_{k\in S} f (k)\end{aligned}
k∈S∑f(k) 与
∑
k
∈
S
g
(
k
)
\begin{aligned}\sum_{k\in S} g (k)\end{aligned}
k∈S∑g(k)
由于答案可能很大, 只需要输出答案对
1
e
9
+
7
1e9+7
1e9+7取模的结果.
n ≤ 1000 , n\leq1000, n≤1000,矩阵中每个元素大小在 i n t int int范围内
S o l u t i o n \mathcal{Solution} Solution
对于有位运算操作的题目,计算贡献时首先考虑的方法就是按位计算贡献
因为某一位的贡献是不会受到其他位影响的,而整体计算因为位运算的特殊性,还是会考虑到按位计算
我们枚举每一位
k
k
k,然后把矩阵换成
0
/
1
0/1
0/1矩阵,其中
0
0
0元素表示原本矩阵中这一元素在枚举到的这位二进制表示下为
0
0
0,为
1
1
1则为
1
1
1
考虑这个
0
/
1
0/1
0/1矩阵中有多少个子矩阵会被计算贡献,最后把这个数乘以
2
k
2^k
2k
对于与运算,就是要求里面有多少个全
1
1
1矩阵
对于或运算,就是要求里面有多少个有
1
1
1矩阵
与运算和或运算实际上可以互相转换
全
1
1
1矩阵数=所有矩阵数-有
0
0
0矩阵数
有
1
1
1矩阵数=所有矩阵数-全
0
0
0矩阵数
设
a
l
l
all
all为
n
×
n
n\times n
n×n的矩阵所包含的所有子矩阵
显然
a
l
l
=
(
n
(
n
+
1
)
2
)
2
all=(\frac{n(n+1)}{2})^2
all=(2n(n+1))2
之后考虑怎么计算满足条件的矩阵个数
55
55
55分解法
O
(
l
o
g
2
n
⋅
n
3
)
O(log_2n\cdot n^3)
O(log2n⋅n3)
考虑枚举矩阵的上边界,下边界
我们计算在这样的矩阵中满足要求的个数
有
1
1
1矩阵显然容易做些
考虑枚举以一个矩阵中最左边的
1
1
1出现位置
这样可以不重不漏全部计算
显然,只要包含了这个有
1
1
1的一列,当前范围内所有矩阵都是合法的
注意不要和上一个有
1
1
1的那列重复了
用
s
u
m
[
i
]
[
j
]
sum[i][j]
sum[i][j]表示第
j
j
j列到第
i
i
i个位置共有多少
1
1
1
只要
s
u
m
[
下
边
界
]
[
当
前
列
]
−
s
u
m
[
上
边
界
−
1
]
[
当
前
列
]
>
0
sum[下边界][当前列]-sum[上边界-1][当前列]>0
sum[下边界][当前列]−sum[上边界−1][当前列]>0就说明这一列有
1
1
1
其他的很简单,具体的过会儿看代码吧
100 100 100分解法 O ( l o g 2 n ⋅ n 2 ) O(log_2n\cdot n^2) O(log2n⋅n2)
考虑枚举矩阵的下边界
计算全
1
1
1矩阵
用
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示第
j
j
j列,从
i
i
i开始往上有多少个连续的
1
1
1
然后枚举到哪一列了,用
c
n
t
cnt
cnt表示每次会增加多少个矩阵
用单调栈维护一下即可
C o d e \mathcal{Code} Code
55
55
55分
试图卡常碰运气版
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月10日 星期二 14时17分44秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
#define rint register int
using namespace std;
const int maxn = 1003;
const int mod = 1000000007;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int n,mx,all,ans1,ans2;
int lt[maxn],up[maxn];
int a[maxn][maxn],sum[maxn][maxn];
bool hav[maxn];
bool b[maxn][maxn];
//{{{calc
inline int calc (int mi,bool opt)
{
int res=0;
for (rint i=1;i<=n;++i)
for (int j=1;j<=n;++j){
b[i][j]=(a[i][j]&mi);
b[i][j]^=opt;
sum[i][j]=sum[i-1][j]+b[i][j];
}
for (rint i=1;i<=n;++i)//上边界
for (rint j=i;j<=n;++j){//下边界
int last=0;
for (rint k=1;k<=n;++k)//第一个1
if (sum[j][k]-sum[i-1][k]) res=(res+(k-last)*(n-k+1))%mod,last=k;
}
return res;
}
//}}}
int main()
{
cin>>n;
for (rint i=1;i<=n;++i)
for (rint j=1;j<=n;++j)
cin>>a[i][j],mx=max(mx,a[i][j]);
all=n*(n+1)/2%mod;
all=1ll*all*all%mod;
for (rint k=0;k<=31;++k){
int t=1<<k;
if (t>mx) break;
int num=calc(t,1);
num=(all-num+mod)%mod;
ans1=(ans1+1ll*num*t%mod)%mod;
num=calc(1<<k,0);
ans2=(ans2+1ll*num*t%mod)%mod;
}
printf("%d %d\n",ans1,ans2);
return 0;
}
100 100 100分
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月10日 星期二 14时17分44秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn = 1003;
const int mod = 1000000007;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int n,mx,all,ans1,ans2;
int stk[maxn];
int a[maxn][maxn],f[maxn][maxn];
bool b[maxn][maxn];
//{{{calc
inline int calc (int mi,bool opt)
{
int res=0;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j){
b[i][j]=(a[i][j]&mi);
b[i][j]^=opt;
f[i][j]=b[i][j]?f[i-1][j]+1:0;
}
for (int i=1;i<=n;++i){
int cnt=0,top=0;
for (int j=1;j<=n;++j){
cnt+=f[i][j];
while (top&&f[i][stk[top]]>f[i][j]) cnt-=(stk[top]-stk[top-1])*(f[i][stk[top]]-f[i][j]),--top;
res=(res+cnt)%mod;
stk[++top]=j;
}
}
return res;
}
//}}}
int main()
{
cin>>n;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
cin>>a[i][j],mx=max(mx,a[i][j]);
all=n*(n+1)/2%mod;
all=1ll*all*all%mod;
for (int k=0;k<=31;++k){
int t=1<<k;
if (t>mx) break;
int num=calc(t,0);
ans1=(ans1+1ll*num*t%mod)%mod;
num=calc(1<<k,1);
num=(all-num+mod)%mod;
ans2=(ans2+1ll*num*t%mod)%mod;
}
printf("%d %d\n",ans1,ans2);
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧