1. 程式人生 > >[GX/GZOI2019]與或和(單調棧+按位運算)

[GX/GZOI2019]與或和(單調棧+按位運算)

++ calc ide namespace class db4 names 圖片 d+

首先看到與或,很顯然想到按照位拆分運算。然後就變成了0/1矩陣,要使矩陣在當前位與為1,則矩陣全為1,如果是或為1,則是矩陣不全為0,然後求全為0/1的矩陣個數即可。記錄c[i][j]表示以a[i][j]在該位向上0/1的長度。然後對於每一行,單調棧求解即可。

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int N=1003,mod=1e9+7;
int n,ans1,ans2,top,a[N][N],b[N][N],c[N][N],st[N],sum[N];
int calc()
{
    for(int i=1;i<=n;i++)
    
for(int j=1;j<=n;j++) c[i][j]=b[i][j]?c[i-1][j]+1:0; int ret=0; for(int i=1;i<=n;i++) { st[0]=top=0; for(int j=1;j<=n;j++) if(!c[i][j])st[0]=j,top=0; else{ while(top&&c[i][j]<=c[i][st[top]])top--; st[++top]=j,sum[top]=(sum[top-1
]+1ll*(j-st[top-1])*c[i][j])%mod; ret=(ret+sum[top])%mod; } } return ret; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); for(int t=0;t<=30;t++) { for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) b[i][j]=(a[i][j]>>t)&1; ans1=(ans1+(1ll<<t)*calc())%mod; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) b[i][j]^=1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans2=(ans2+1ll*(n-i+1)*(n-j+1)%mod*(1ll<<t))%mod; ans2=(ans2-(1ll<<t)*calc()%mod+mod)%mod; } printf("%d %d",ans1,ans2); }
View Code

[GX/GZOI2019]與或和(單調棧+按位運算)