看到位運算就直接拆位,因而問題變成了求有多少個全\(0\)子矩陣和有多少個全\(1\)子矩陣。
這兩個操做本質就是同樣的,不妨考慮有多少個全\(1\)子矩陣。
預處理出每一個元素向上可以找的最多的\(1\)的個數,對於每一行從作往右掃一遍,拿一個單調棧維護一下,這樣子就能夠計算出以每一個元素爲右下角時的貢獻了。
時間複雜度\(O(n^2logV)\),在BZOJ上由於常數太大T了QwQ。ios
#include<iostream> #include<cstdio> using namespace std; #define MAX 1010 #define MOD 1000000007 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,a[MAX][MAX],b[MAX][MAX],u[MAX][MAX]; int Q[MAX],h,t,ans1=0,ans2=0; int main() { n=read(); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)a[i][j]=read(); for(int k=0;k<31;++k) { for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)b[i][j]=(a[i][j]>>k)&1; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(!b[i][j])u[i][j]=0; else u[i][j]=u[i-1][j]+1; for(int i=1;i<=n;++i) { Q[h=t=1]=0;int now=0; for(int j=1;j<=n;++j) { while(h<t&&u[i][Q[t]]>=u[i][j])now=(now+MOD-1ll*(Q[t]-Q[t-1])*u[i][Q[t]]%MOD)%MOD,--t; now=(now+1ll*(j-Q[t])*u[i][j])%MOD;Q[++t]=j; ans1=(ans1+1ll*now*(1<<k))%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) if(!b[i][j])u[i][j]=0; else u[i][j]=u[i-1][j]+1; ans2=(ans2+1ll*(1<<k)%MOD*(1ll*n*(n+1)/2*n*(n+1)/2%MOD)%MOD)%MOD; for(int i=1;i<=n;++i) { Q[h=t=1]=0;int now=0; for(int j=1;j<=n;++j) { while(h<t&&u[i][Q[t]]>=u[i][j])now=(now+MOD-1ll*(Q[t]-Q[t-1])*u[i][Q[t]]%MOD)%MOD,--t; now=(now+1ll*(j-Q[t])*u[i][j])%MOD;Q[++t]=j; ans2=(ans2+MOD-1ll*now*(1<<k)%MOD)%MOD; } } continue; } printf("%d %d\n",ans1,ans2); return 0; }