「GXOI / GZOI2019」与或和

问题 C: 与或和

时间限制: 4 Sec  内存限制: 512 MB
提交: 278  解决: 78
[提交] [状态] [命题人:admin]

题目描述

Freda 学习了位运算和矩阵以后,决定对这种简洁而优美的运算,以及蕴含深邃空间的结构进行更加深入的研究。

对于一个由非负整数构成的矩阵,她定义矩阵的AND值为矩阵中所有数二进制AND(&)的运算结果;定义矩阵的OR值为矩阵中所有数二进制OR(|)的运算结果。

给定一个N×N的矩阵,她希望求出:

该矩阵的所有子矩阵的AND值之和(所有子矩阵AND值相加的结果)。
该矩阵的所有子矩阵的OR值之和(所有子矩阵OR值相加的结果)。
接下来的剧情你应该已经猜到——Freda并不想花费时间解决如此简单的问题,所以这个问题就交给你了。

由于答案可能非常的大,你只需要输出答案对109+7取模后的结果。

 

输入

第一行是一个正整数N,表示矩阵的尺寸。
接下来N行,每行N个自然数,代表矩阵的一行。相邻两个自然数之间由一个或多个空格隔开。

 

输出

输出只有一行,包含两个用空格隔开的整数,第一个应为所有子矩阵AND值之和除以 109+7的余数,第二个应为所有子矩阵OR值之和除以109+7的余数。

 

样例输入

复制样例数据

3
1 0 0
0 0 0
0 0 0

样例输出

1 9

 

提示

该3×3矩阵共有9个1×1子矩阵、6个1×2子矩阵、6个2×1子矩阵4个2×2子矩阵、3个1×3子矩阵、3 个3×1子矩阵、2个2×3子矩阵、2个3×2子矩阵和1个3×3子矩阵。
只有一个子矩阵(仅由第一行第一列的那个元素构成的1×1矩阵)AND值为1,其余子矩阵的AND值均为0,总和为1。
包含第一行第一列那个元素的子矩阵有9个,它们的OR值为1,其余子矩阵的OR值为0 ,总和为9。

 

 

 

 

 

写完之后发现被卡常了,

如果这里把i,j互换,那就t了(upc上倒是没死掉,但是也慢了300ms,不知道为啥某人的这里改了之后反而变快了(在upc上,libre上也是t掉的))


for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++) mmp[i][j] = ((a[i][j] & 1) == p) ? 1 + mmp[i][j - 1] : 0;

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#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;
const int maxn = 1e3 + 7, mod = 1e9 + 7;
int mmp[maxn][maxn], n, x;
int a[maxn][maxn];
int Stk[maxn];
int Up[maxn], Dn[maxn];
int read() {
    int 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()
ll Add, ans[2];
ll sovle(int p) {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) mmp[i][j] = ((a[i][j] & 1) == p) ? 1 + mmp[i][j - 1] : 0;
    ll tmp = 0;
    int Top = 0;
    for (int j = 1; j <= n; j++) {
        Top = 0;
        for (int i = 1; i <= n; i++) {
            if (mmp[i][j]) {
                Up[i] = 1;
                while (Top && mmp[i][j] < mmp[Stk[Top - 1]][j]) Up[i] += Up[Stk[--Top]];
                Stk[Top++] = i;
            } else
                Top = Up[i] = 0;
        }
        Top = 0;
        for (int i = n; i; i--) {
            if (mmp[i][j]) {
                Dn[i] = 1;
                while (Top && mmp[i][j] <= mmp[Stk[Top - 1]][j]) Dn[i] += Dn[Stk[--Top]];
                Stk[Top++] = i;
            } else
                Top = Dn[i] = 0;
            tmp += 1ll * Up[i] * Dn[i] * mmp[i][j] % mod;
            if (tmp >= mod)
                tmp -= mod;
        }
    }
    return tmp;
}
int main() {
    // scanf("%d",&n);
    n = read;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            // scanf("%d",&a[i][j]);
            a[i][j] = read;
    Add = 1ll * n * n * (n + 1) * (n + 1) / 4 % mod;
    ll ans1 = 0, ans2 = 0;
    for (int k = 0, tp = 1; k <= 30; k++, tp <<= 1) {
        ans1 += sovle(1) * tp % mod;
        if (ans1 >= mod)
            ans1 -= mod;
 
        ans2 += (Add - sovle(0) + mod) % mod * tp % 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;
}

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值