Skip to content

题解-CF1368G

Posted on:2023年7月22日 at 10:56

本文章遵守知识共享协议 CC-BY-NC-SA,同步发表于洛谷题解区,转载时须在文章的任一位置附上原文链接和作者署名(rickyxrc)。推荐在我的个人博客阅读。

人生第一道 3200!

题面大意

有一个已经铺满多米诺骨牌的棋盘,你可以拿下来一块骨牌,并将剩下的骨牌移动来填补空位,但是每个骨牌只能移动最多一位,求可能得到的本质不同的方案数。

解题思路

我们首先这么想:一个骨牌滑动一步,相当于对应的空位移动两步。

然后我们不难发现两个相邻的点,其 xy 坐标奇偶性是不同的,在坐标变化 2 之后奇偶性仍然是不同的。

这就代表着两个点是不会走到相同的地方的,我们可以分开计算。

此时我们参考建图的思路,对每个点(作为空位时)向能到达的点连边。

容易证明,建出来的图一定是一个森林,因为图中不存在环(没有方案可以移动骨牌使得恰好回到原位)且每个点的出度最大为 1。

每个点能到达的点数量就是子树大小,两个点将其相乘即可。

但是可能会出现重复统计的问题(不是两个点重复,而是不同的点对得到了相同的清空),对此我们将每组答案看作一个长宽分别为两颗子树大小的矩形,左下角坐标是两个点的 dfn。

为什么这样建模呢?因为在一棵子树中,所有 dfs 是连续的,所以这个矩形就可以包含所有答案的情况。

最后使用扫描线求解即可。

#include <stdio.h>
#include <vector>
#include <algorithm>

#define maxn 1000007

typedef long long i64;

std::vector<i64> edge[maxn];
char mp[maxn];

i64 n, m;
char ch;

inline i64 hsh(i64 x, i64 y) { return (x - 1) * m + y; }

i64 dfn[maxn], size[maxn], dfncnt, ids[maxn], idcnt, indeg[maxn];
i64 res;

std::vector<std::pair<std::pair<i64, i64>, std::pair<i64, i64>>> points;

void dfs(i64 index)
{
    size[index] = 1;
    dfn[index] = ++dfncnt;
    for (auto u : edge[index])
        dfs(u),
            size[index] += size[u];
}

#define ls (index << 1)
#define rs (ls | 1)
#define mid ((l + r) >> 1)

i64 xtmp[maxn << 2];

struct stree
{
    i64 data, lazy, l, r;
} trees[maxn << 2];

struct sq
{
    i64 x, y1, y2, vl;
} seq[maxn << 2];

inline bool cmp(sq a, sq b)
{
    return a.x < b.x;
}

void pushup(i64 index)
{
    if (trees[index].lazy > 0)
        trees[index].data = trees[index].r - trees[index].l;
    else
        trees[index].data = trees[ls].data + trees[rs].data;
}

void build(i64 index, i64 l, i64 r);

inline i64 min(i64 a, i64 b);
inline i64 max(i64 a, i64 b);

void update(i64 index, i64 y1, i64 y2, i64 vl);

i64 linecnt;
void add_line(i64 xxx1, i64 yyy1, i64 xxx2, i64 yyy2, i64 _sum)
{
    seq[++linecnt].x = xxx1;
    seq[linecnt + _sum].x = xxx2;
    seq[linecnt].y1 = seq[linecnt + _sum].y1 = yyy1;
    seq[linecnt].y2 = seq[linecnt + _sum].y2 = yyy2;
    seq[linecnt].vl = 1;
    seq[linecnt + _sum].vl = -1;
    xtmp[linecnt] = yyy1;
    xtmp[linecnt + _sum] = yyy2;
}

i64 a, b, l1, l2, r1, r2, siz_;

int main()
{
    scanf("%d%d", &n, &m);
    for (i64 i = 1; i <= n; i++)
        for (i64 j = 1; j <= m; j++)
        {
            do
            {
                ch = getchar();
            } while (ch != 'U' && ch != 'D' && ch != 'L' && ch != 'R');
            mp[hsh(i, j)] = ch;
        }

    for (i64 i = 1; i <= n; i++)
        for (i64 j = 1; j <= m; j++)
        {
            if (i + 2 <= n && mp[hsh(i + 1, j)] == 'U' && mp[hsh(i + 2, j)] == 'D')
                indeg[hsh(i + 2, j)]++,
                    edge[hsh(i, j)].push_back(hsh(i + 2, j));
            if (i - 2 >= 1 && mp[hsh(i - 1, j)] == 'D' && mp[hsh(i - 2, j)] == 'U')
                indeg[hsh(i - 2, j)]++,
                    edge[hsh(i, j)].push_back(hsh(i - 2, j));
            if (j + 2 <= m && mp[hsh(i, j + 1)] == 'L' && mp[hsh(i, j + 2)] == 'R')
                indeg[hsh(i, j + 2)]++,
                    edge[hsh(i, j)].push_back(hsh(i, j + 2));
            if (j - 2 >= 1 && mp[hsh(i, j - 1)] == 'R' && mp[hsh(i, j - 2)] == 'L')
                indeg[hsh(i, j - 2)]++,
                    edge[hsh(i, j)].push_back(hsh(i, j - 2));
            if (mp[hsh(i, j)] == 'L')
                points.push_back({{i, j}, {i, j + 1}});
            if (mp[hsh(i, j)] == 'U')
                points.push_back({{i, j}, {i + 1, j}});
        }

    for (i64 i = 1; i <= n; i++)
        for (i64 j = 1; j <= m; j++)
            if (!indeg[hsh(i, j)])
                dfs(hsh(i, j));

    for (auto u : points)
    {
        a = hsh(u.first.first, u.first.second), b = hsh(u.second.first, u.second.second);
        l1 = dfn[a], r1 = dfn[a] + size[a];
        l2 = dfn[b], r2 = dfn[b] + size[b];
        if ((u.first.first + u.first.second) & 1)
            add_line(l2, l1, r2, r1, (n * m) >> 1);
        else
            add_line(l1, l2, r1, r2, (n * m) >> 1);
    }

    siz_ = points.size();

    std::sort(xtmp + 1, xtmp + 2 * siz_ + 1);
    std::sort(seq + 1, seq + 2 * siz_ + 1, cmp);
    build(1, 1, 2 * siz_);

    update(1, seq[1].y1, seq[1].y2, seq[1].vl);

    for (i64 i = 2; i <= 2 * siz_; i++)
    {
        res += 1ll * (seq[i].x - seq[i - 1].x) * trees[1].data;
        update(1, seq[i].y1, seq[i].y2, seq[i].vl);
    }

    printf("%lld", res);

    return 0;
}


在 Rickyxrc's blog 出现的文章,若无特殊注明,均采用 CC BY-NC-SA 4.0 协议共享,也就是转载时需要注明本文章的地址,并且引用本文章的文章也要使用相同的方式共享。