upc 个人训练赛第一场 C.与或和(找到全是某个元素的子矩阵的个数-小技巧+单调栈)

题意:给你一个n*n(n<=1000)的矩阵,要求求出所有子矩阵的OR和 以及 所有子矩阵的AND和

题解:子矩阵的AND和 可以将矩阵的每一个元素拆成30位二进制 对于每一位二进制讨论贡献 那么每存在一个子矩阵全是1 那么就会对答案贡献乘以这一位的权值 那么这里就设计到一个小技巧 -- 找一个矩阵全是1的子矩阵个数 不会这个知识点便立马学习了一下 受到博客 https://blog.csdn.net/qq_42814118/article/details/81349964 的启发, 可以枚举每一列 然后枚举一行 从这一行作为矩阵的最大宽度 然后上下延伸(保持单向性防止重复) 延伸的长度乘以宽度便是这个位置的所有矩阵的贡献 同时要事先预处理出来每一行连续1的个数 OR的和便是可以用总的个数减去全是0的子矩阵的个数然后乘以每一位的贡献即可(卡常,注意取模的个数)

 

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
  
ll a[1005][1005],num[1005][1005];
int n,up[1005],down[1005],st[1005];
  
ll calc(int v){
     
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            num[i][j] = ((a[i][j]&1)==v)?num[i][j-1]+1:0;
        }
    }
      
    ll sum = 0;
    int top = 0;
    for(int j=1;j<=n;j++){
        top = 0;
        for(int i=1;i<=n;i++){
            if(num[i][j]){
                up[i] = 1; 
                while(top && num[i][j]<num[st[top]][j]) up[i]+=up[st[top--]]; // 单调栈维护 然后合并 
                st[++top] = i;
            }else{
                top = 0;
                up[i] = 0;
            }
        }
        top = 0;
        for(int i=n;i>=1;i--){
            if(num[i][j]){
                down[i] = 1;
                while(top && num[i][j]<=num[st[top]][j]) down[i]+=down[st[top--]]; //延伸时保持单向性 与上面不一致即可 
                st[++top] = i;
            }else{
                top = 0;
                down[i] = 0;
            }
            sum += 1LL*up[i]*down[i]*num[i][j]%mod;
            if(sum>=mod) sum -= mod;
        }
    }
    return sum;
}
  
int Sheryang(){
      
    n = read;
      
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            a[i][j] = read;
        }
    }
      
    ll ans1 = 0,ans2 = 0;
      
    ll tot = 1LL*n*(n+1)/2*n*(n+1)/2%mod;//总矩阵的个数 
    for(int i=0,tmp=1;i<31;i++,tmp<<=1){
         
        ans1 += calc(1)*tmp%mod;
        if(ans1>=mod) ans1 -= mod;
         
        ans2 += (tot-calc(0)%mod+mod)%mod*tmp%mod;
        if(ans2>=mod) ans2 -= mod;
         
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                a[i][j] >>= 1;
            }
        }
    }
    
    printf("%lld %lld\n",ans1,ans2);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章