luogu P5300 [GXOI/GZOI2019]与或和
题面传送门
一眼看数据范围感觉像一只log
然后因为是位运算所以考虑分开拆位考虑。
分开后就是求矩阵内全\(0\)与全\(1\)的个数。
然后这个东西其实是单调栈经典问题,就是在单调栈预处理离当前向上距离最近的那个然后直接转移,中间那一段随便转移即可。
时间复杂度\(O(n^2logw)\)
code:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db double
#define S 10000000
#define N 1000
#define eps (1e-6)
#define mod 1000000007
using namespace std;
int n,m,a[N+5][N+5],f[N+5],g[N+5],st[N+5],head;ll ans1,ans2,tot,pus,dp[N+5];
int main(){
freopen("1.in","r",stdin);
re int i,j,k;scanf("%d",&n);for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&a[i][j]);
for(i=1;i<=n;i++) tot=(tot+n*(n+1)/2*(i))%mod;
for(k=0;k<=30;k++){
for(memset(f,0,sizeof(f)),pus=0,i=1;i<=n;i++){
for(j=1;j<=n;j++)(a[i][j]>>k&1)?(f[j]++):(f[j]=0);
for(head=0,j=1;j<=n;j++) {
while(head&&f[st[head]]>f[j]) head--;
pus+=(dp[j]=(dp[st[head]]+(j-st[head])*f[j])%mod);st[++head]=j;
}
pus%=mod;
}
ans1+=pus*(1<<k)%mod;
for(memset(f,0,sizeof(f)),pus=0,i=1;i<=n;i++){
for(j=1;j<=n;j++)(a[i][j]>>k&1)?(f[j]=0):(f[j]++);
for(head=0,j=1;j<=n;j++) {
while(head&&f[st[head]]>f[j]) head--;
pus+=(dp[j]=(dp[st[head]]+(j-st[head])*f[j])%mod);st[++head]=j;
}
pus%=mod;
}
ans2+=(tot-pus+mod)*(1<<k)%mod;
}
printf("%lld %lld\n",ans1%mod,ans2%mod);
}