[LOJ3083][GXOI/GZOI2019]與或和——單調棧
阿新 • • 發佈:2019-04-17
cst aps cto top const 鏈接 https cpp ()
題目鏈接:
[GXOI/GZOI2019]與或和
既然求的是二進制運算的和,那麽我們按位考慮,這樣就將矩陣變成了一個$01$矩陣。
對於或運算,就是求有多少個子矩形中有$1$。
直接求不好辦,考慮有多少個矩形只有$0$。
我們統計以每個$0$為矩形右下角的矩形有多少個。
對於第$i$行的每一列維護出從這一行開始往上有多少個連續的$0$。
現在問題就變成了對於第$i$行的每一列,之前有多少個格子能作為矩形的左上角和它匹配。
用單調棧維護一個單調遞增的序列對每行分別統計答案即可。
對於與運算,就是將總子矩形數$-$包含$0$的子矩形數,對$0$再做一遍即可。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define pr pair<int,int> using namespace std; const int mod=1e9+7; ll ans1,ans2; int a[1010][1010]; int b[1010][1010]; int n; int mx[1010]; int top; pr st[1010]; ll sum; ll find1() { memset(mx,0,sizeof(mx)); ll res=0; for(int i=1;i<=n;++i) { ll num=0; top=0; for(int j=1;j<=n;++j) { mx[j]=b[i][j]?mx[j]+1:0; int len=1; while(top&&st[top].first>=mx[j]) { num-=st[top].first*st[top].second; len+=st[top].second; top--; } num+=mx[j]*len; res=(res+num)%mod; st[++top]=make_pair(mx[j],len); } } return res; } ll find2() { memset(mx,0,sizeof(mx)); ll res=0; for(int i=1;i<=n;++i) { ll num=0; top=0; for(int j=1;j<=n;++j) { mx[j]=(!b[i][j])?mx[j]+1:0; int len=1; while(top&&st[top].first>=mx[j]) { num-=st[top].first*st[top].second; len+=st[top].second; top--; } num+=mx[j]*len; res=(res+num)%mod; st[++top]=make_pair(mx[j],len); } } return res; } int main() { scanf("%d",&n); sum=1ll*n*(n+1)/2*n*(n+1)/2; sum%=mod; for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { scanf("%d",&a[i][j]); } } for(int k=0;k<=31;++k) { for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { if(a[i][j]&(1<<k)) { b[i][j]=1; } else { b[i][j]=0; } } } ans1+=(1ll<<k)%mod*find1()%mod,ans1%=mod; ans2+=(1ll<<k)%mod*(sum-find2()+mod)%mod,ans2%=mod; } ans1=(ans1%mod+mod)%mod,ans2=(ans2%mod+mod)%mod; printf("%lld %lld",ans1,ans2); }
[LOJ3083][GXOI/GZOI2019]與或和——單調棧