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;
}

 

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