HDU 6052 To my boyfriend

Posted on By 二价氢

【题意】

给你一个矩阵,问随机一个子矩阵的颜色个数的期望数。

【Solution】

期望转计数问题。

考虑这样一种计数方式,对于任意一个子矩阵中的某一个颜色c,我们只在其从左往右,从上往下的第一次出现计算它。

于是反过来,我们可以计算每一个格子在多少个子矩阵中被计算了。

hdu6052-sol-01.png

如上图所示,如果我们要计算浅绿色格子,则四个深绿色格子不能参与计算。

通过维护每个颜色在每一行最后一次出现的位置,我们可以维护右边的x的轮廓,进而维护当左边界固定为某一列时,上边界和下边界的位置。

这个维护可以用暴力维护,复杂度是$(n^3)$,常数不是特别大,比标程的$O(n^4 / 13)$优。

【Code】

耗时:124 ms

内存:6256 K

#include <bits/stdc++.h>
using namespace std;

const int maxn = 105;
int mp[maxn][maxn];
int ls[maxn * maxn][maxn];
//int up[maxn * maxn][maxn], dn[maxn * maxn][maxn];
int pref[maxn], suff[maxn], upper[maxn], lower[maxn];
long long ans = 0;
int n, m;
double sol()
{
    ans = 0;
    memset(ls, 0, sizeof ls);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d", &mp[i][j]);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            int co = mp[i][j];

            memset(upper, 0, sizeof upper);
            memset(lower, 0x3f, sizeof lower);
            memset(pref, 0, sizeof pref);
            memset(suff, 0, sizeof suff);
            pref[j] = ls[co][j];
            for (int k = j - 1; k >= 1; k--)
                pref[k] = max(pref[k + 1], ls[co][k]);
            for (int k = 0; k <= j; k++)
                upper[pref[k]] = k + 1;
            upper[i] = max(upper[i], 1);
            for (int k = i - 1; k >= 1; k--)
                upper[k] = max(upper[k], upper[k + 1]);
            //
            suff[j] = ls[co][j];
            for (int k = j + 1; k <= m; k++)
                suff[k] = max(suff[k - 1], ls[co][k]);
            for (int k = m; k >= j; k--)
                lower[suff[k]] = k - 1;
            lower[i] = min(lower[i], m);
            for (int k = i - 1; k >= 1; k--)
                lower[k] = min(lower[k], lower[k + 1]);
            for (int k = 1; k <= i; k++)
            {
                if (upper[k] > j || lower[k] < j) continue;
                int up = j - upper[k] + 1;
                int dn = lower[k] - j + 1;
                ans += up * dn * (n - i + 1);
            }
            ls[co][j] = i;

        }
    }
    long long tot = (n + 1ll) * (m + 1ll) * n * m;
    return 4. * ans / (tot);
}


int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
        printf("%.9f\n", sol());
    return 0;
}