ZOJ - 3955 Saddle Point 每个值对答案的贡献

本文介绍了一种高效算法,用于解决特定类型的矩阵问题。给定一个n*m的矩阵,算法的目标是找出所有满足既是所在行最小值又是所在列最大值的元素数量。通过巧妙地利用排序和快速幂运算,该算法能够有效避免枚举所有可能的子矩阵组合,从而大大减少了计算复杂度。

题意:

给定n*m的 矩阵,

选择一行或者若干行,一列或者若干列,构成新的矩阵,问这个矩阵中,满足某个元素是本行最小本列最大的这个性质的所有元素的个数


思路:

题中说了,所有的矩阵的可能性 有 (2^n - 1) * (2^m - 1)中,显然没法枚举;

所以 我们考虑某一个元素,会在那个矩阵中出现,也就是算这个元素满足上面性质时 对答案的贡献值是多少;

这样我们可以找到一个值推一下,他在本行中满足最小这个条件,那本行中比他大的所有值 所在的那一列跟他都可以组合形成矩阵,

同理在这一列中比他小的的所有值所在的哪一行也可以跟他任意组合 形成矩阵

加个快速幂



#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<queue>
#include<stack>
#include<map>
#define PI acos(-1.0)
#define in freopen("in.txt", "r", stdin)
#define out freopen("out.txt", "w", stdout)
#define kuaidian ios::sync_with_stdio(0);

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e6, maxd = 1000 + 7;
const ll mod = 1e9 + 7;
const int INF = 0x7f7f7f7f;

int n, m;
int a[maxd][maxd];
int b[maxd][maxd], c[maxd][maxd];

ll pow_(ll x, int n) {
    ll ans = 1;
    while(n) {
        if(n&1) ans = (ans*x) % mod;;
        x = (x * x) % mod;
        n /= 2;
    }
    return ans;
}

void solve() {
    int anss = 0;
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= m; ++j) {
            int t1 = m - (upper_bound(b[i]+1, b[i]+1+m, a[i][j]) - (b[i]+1) );
            int t2 = (lower_bound(c[j]+1, c[j]+1+n, a[i][j]) - (c[j]+1) );
            anss = (anss + (pow_(2, t1))*(pow_(2, t2)) ) % mod;
        }
    }
    cout << anss << endl;
}


int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                scanf("%d", &a[i][j]);
                b[i][j] = a[i][j];
                c[j][i] = a[i][j];
            }
        }
        for(int i = 1; i <= n; ++i) {
            sort(b[i]+1, b[i]+1+m);
        }
        for(int i = 1; i <= m; ++i) {
            sort(c[i]+1, c[i]+1+n);
        }
        solve();
    }
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值