23.8.10 杭电暑期多校8部分题解

1008 - Expectation of Rank

题目大意

一个 n ∗ n n*n nn 的矩阵在 m o d   p mod\space p mod p 意义下随机生成,问矩阵的秩的期望为多少

解题思路

某一行要对秩产生贡献,那肯定与之前所有行之间都不存在线性相关

由此考虑dp, f i , j f_{i,j} fi,j 表示前 i i i 行产生 j j j 贡献的方案数

显然可能从 f i − 1 , j f_{i-1,j} fi1,j f i − 1 , j − 1 f_{i-1,j-1} fi1,j1 两种状态转移过来

f i − 1 , j f_{i-1,j} fi1,j 需要第 i i i 行不产生贡献,即和之前的 i − 1 i-1 i1 行线性相关,方案数是 p j p^j pj

f i − 1 , j − 1 f_{i-1,j-1} fi1,j1 需要第 i i i 行产生贡献,即和之前的 i − 1 i-1 i1 行线性无关

容斥考虑,总共可能的情况是 p n p^n pn,线性相关方案数是 p j − 1 p^{j-1} pj1,那么线性无关方案数就是 p n − p j − 1 p^n-p^{j-1} pnpj1

最后统计答案就是 ∑ i = 1 n f n , i ∗ i p n ∗ n \frac{\sum_{i=1}^{n}f_{n,i}*i}{p^{n*n}} pnni=1nfn,ii

code

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 5009;
int t, n;
long long p, f[N][N], sum, g[N];
long long madd(long long a, long long b) {return a + b >= MOD ? a + b - MOD : a + b;}
long long msub(long long a, long long b) {return a - b < 0 ? a - b + MOD : a - b;}
long long mmul(long long a, long long b) {return a * b % MOD;}
long long pw(long long a, int b) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
int main() {
    scanf("%d", &t);
    while (t --) {
        scanf("%d%lld", &n, &p);
        g[0] = 1;
        for (int i = 1; i <= n; ++ i) g[i] = g[i - 1] * p % MOD;
        f[0][0] = 1;
        for (int i = 1; i <= n; ++ i) {
            f[i][0] = 1;
            for (int j = 1; j <= i; ++ j)
                f[i][j] = madd(mmul(f[i - 1][j], g[j]), mmul(f[i - 1][j - 1], msub(g[n], g[j - 1])));
        }
        sum = 0;
        for (int i = 1; i <= n; ++ i)
            sum = madd(sum, mmul(f[n][i], i));
        printf("%lld\n", sum * pw(pw(p, n * n), MOD - 2) % MOD);
    }
    return 0;
}

1009 - Diagonal Fancy

题目大意

有一个 n ∗ m n*m nm 的矩阵,求其中有多少个连续子正方形,满足该正方形内每个从左上到右下的斜线上数字相同,且不同斜线之间数字互不相同

解题思路

可以发现一个大正方形的答案需要从小正方形转移过来,那么很容易可以想到需要dp

f i , j f_{i,j} fi,j 表示左上端点为 ( i ,   j ) (i,\space j) (i, j) 的最大正方形边长为多少

如果暂时不考虑不同斜线之间数字互不相同

深度考虑一下,其实 f i , j f_{i,j} fi,j 可以维护从 ( i ,   j ) (i,\space j) (i, j) 向下,向右一直两两不同,沿对角线一直相等的最大长度

i = n i=n i=n 或者 j = m j=m j=m 或者 a i , j ≠ a i + 1 , j + 1 a_{i,j}\ne a_{i+1,j+1} ai,j=ai+1,j+1 时,显然 f i , j = 1 f_{i,j}=1 fi,j=1

a i , j = a i + 1 , j + 1 a_{i,j}=a_{i+1,j+1} ai,j=ai+1,j+1 时,需要去找向下,向右,沿对角线成立的最大长度

这三者的值分别为 f i + 1 , j + 1 ,   f i , j + 1 + 1 ,   f i + 1 , j + 1 + 1 f_{i+1,j}+1,\space f_{i,j+1}+1,\space f_{i+1,j+1}+1 fi+1,j+1, fi,j+1+1, fi+1,j+1+1,需要取三者中最小的

因此 f i , j = m i n { f i + 1 , j ,   f i , j + 1 ,   f i + 1 , j + 1 } + 1 f_{i,j}=min\{f_{i+1,j},\space f_{i,j+1},\space f_{i+1,j+1}\} + 1 fi,j=min{fi+1,j, fi,j+1, fi+1,j+1}+1

现在考虑如何去除不同斜线之间数字出现相同的情况

假设 f i , j f_{i,j} fi,j 最后的值为为 r r r

f i + 1 , j f_{i+1,j} fi+1,j 其实保证了 ( i + 1 ,   j ) (i+1,\space j) (i+1, j) ( i + r , j + r − 1 ) (i+r,j+r-1) (i+r,j+r1) 没有相同数字的斜线

f i , j + 1 f_{i,j+1} fi,j+1 其实保证了 ( i ,   j + 1 ) (i,\space j+1) (i, j+1) ( i + r − 1 , j + r ) (i+r-1,j+r) (i+r1,j+r) 没有相同数字的斜线

f i + 1 , j + 1 f_{i+1,j+1} fi+1,j+1 其实保证了 ( i + 1 ,   j + 1 ) (i+1,\space j+1) (i+1, j+1) ( i + r , j + r ) (i+r,j+r) (i+r,j+r) 没有相同数字的斜线

那么其实只要去判断 ( i + r − 1 ,   j ) ,   ( i + r − 2 ,   j ) ,   ( i ,   j + r − 1 ) ,   ( i ,   j + r − 2 ) (i+r-1,\space j),\space(i+r-2,\space j),\space(i,\space j+r-1),\space(i,\space j+r-2) (i+r1, j), (i+r2, j), (i, j+r1), (i, j+r2) 所在的斜线是否会相同

注意 r = 2 r=2 r=2 时需要特殊判断

code

#include <bits/stdc++.h>
using namespace std;
const int N = 1009;
int t, n, m, a[N][N], f[N][N], ans;
inline int read(){
	int x = 0, k = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') k = -1; c = getchar();}
	while (isdigit(c)) {x = x * 10 + c - '0'; c = getchar();}
    x *= k;
    return x;
}
inline int chk(int i, int j, int k) {
    if (k == 2) {
        if (a[i + 1][j] == a[i][j]) return 1;
        if (a[i + 1][j] == a[i][j + 1]) return 1;
        if (a[i][j] == a[i][j + 1]) return 1;
        return 0;
    }
    if (a[i + k - 1][j] == a[i + k - 2][j]) return 1;
    if (a[i + k - 1][j] == a[i][j + k - 2]) return 1;
    if (a[i + k - 1][j] == a[i][j + k - 1]) return 1;
    if (a[i + k - 2][j] == a[i][j + k - 1]) return 1;
    if (a[i][j + k - 2] == a[i][j + k - 1]) return 1;
    return 0;
}
int main() {
    t = read();
    while (t --) {
        scanf("%d%d", &n, &m); ans = 0;
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j)
                a[i][j] = read();
        for (int i = 1; i <= n; ++ i) f[i][m] = 1;
        for (int i = 1; i <= m; ++ i) f[n][i] = 1;
        for (int i = n - 1; i >= 1; -- i)
            for (int j = m - 1; j >= 1; -- j)
                if (a[i][j] != a[i + 1][j + 1]) f[i][j] = 1;
                else {
                    f[i][j] = min(min(f[i + 1][j], f[i][j + 1]), f[i + 1][j + 1]) + 1;
                    if (chk(i, j, f[i][j])) -- f[i][j];
                }
        for (int i = 1; i <= n; ++ i)
            for (int j = 1; j <= m; ++ j)
                ans += f[i][j];
        printf("%d\n", ans);
    }
    return 0;
}

1003 - Congruences

题目大意

m m m 个同余方程组成的方程组 x p i ≡ q i ( m o d   n ) x^{p_i}\equiv q_i(mod\space n) xpiqi(mod n) 的解 x x x,其中 n = ∏ i = 1 m p i n=\prod_{i=1}^m p_i n=i=1mpi p i p_i pi 为质数

解题思路

因为 p i p_i pi n n n 的因数,由同余的性质可以推导 x p i ≡ q i ( m o d   n ) ⇒ x p i ≡ q i ( m o d   p i ) ⇔ x ≡ q i ( m o d   p i ) x^{p_i}\equiv q_i(mod\space n)\Rightarrow x^{p_i}\equiv q_i(mod\space p_i)\Leftrightarrow x\equiv q_i(mod\space p_i) xpiqi(mod n)xpiqi(mod pi)xqi(mod pi)

于是变成了一个很明显的CRT板子题

求出 x x x 后因为无法回推,需要对原来的式子进行验证

注意特判 x = 0 x=0 x=0 时改为 x = n x=n x=n

code

#include <bits/stdc++.h>
#define int __int128
using namespace std;
int t, m, p, q, n, x;
vector <int> v1, v2, v3;
inline int read(){
	int x = 0, k = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') k = -1; c = getchar();}
	while (isdigit(c)) {x = x * 10 + c - '0'; c = getchar();}
    x *= k;
    return x;
}
inline void write(int x) {
    if (x < 0) {putchar('-'); x = -x;}
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
inline int pw(int a, int b, int p) {
    int res = 1;
    while (b) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}
inline int mcrt() {
    int res = 0;
    for (int i = 0; i < m; ++ i)
        res = (res + v2[i] * (n / v1[i]) % n * pw(n / v1[i], v1[i] - 2, v1[i])) % n;
    return res;
}
signed main() {
    t = read();
    while (t --) {
        m = read(); n = 1;
        v1.clear(); v2.clear(); v3.clear();
        for (int i = 1; i <= m; ++ i) {
            p = read(); q = read();
            v1.push_back(p);
            v2.push_back(q % p);
            v3.push_back(q);
            n *= p;
        }
        x = mcrt();
        for (int i = 0; i < m; ++ i)
            if (pw(x, v1[i], n) != v3[i]) {
                x = -1; break;
            }
        if (x == 0) x = n;
        write(x);
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值