Loading

题解:ARC161E Not Dyed by Majority (Cubic Graph)

很有意思的题。

首先考虑 spj 怎么实现,那么要解决两个问题:如何判断无解,如何判断解合法。

对于判断合法,假设 \(u\) 操作后的颜色为 \(c\),相邻的三个点为 \(v_1,v_2,v_3\),那么限制就是操作前这三个点中至少有两个颜色为 \(c\),或者说如果操作前 \(v_1\) 颜色不是 \(c\),那么 \(v_2,v_3\) 的颜色必须是 \(c\)\(v_2,v_3\) 操作前的颜色不是 \(c\) 同理。

那么一个操作后的颜色序列合法,当且仅当不存在满足上述要求的操作前的颜色序列,这就是一个 2-SAT 问题了,可以 \(O(n)\) 解决。

对于判断无解,考虑如果无解,那么说明每个颜色序列都存在一个颜色序列操作一次后得到。这就构成了一个 \(\{0,1\}^n\) 到自己的满射,那么它肯定也是双射。但这是不可能的,因为对于 \(n\) 个点全染黑 和 \(1\) 个点染白剩下 \(n-1\) 个点染黑两种颜色序列,操作一次后的结果都是全黑,那么这不可能是单射,也就不是双射。所以本题不存在无解的情况。

接下来的做法很大胆,但绝大多数人可能都想过(不一定是对于这个题):直接随机答案。

其实仔细想一想上面那个映射,大概可以感觉到有一种对称性,也就是有“一定比例”的颜色序列都不能被映射到。

具体一点,我们的想法是找到一个局部结构,如果这个局部中有 \(a\) 种可能的颜色序列,只能映射到 \(b(b<a)\) 种颜色序列,那么对于全局大概就有 \(\frac{a-b}{a}\) 的序列可以作为答案。

考虑操作前的颜色序列。对于一个点 \(x\)、距离 \(x\)\(1\) 的点 \(y_1,y_2,y_3\)、距离 \(x\)\(2\) 的点 \(z_{1,1},z_{1,2},z_{2,1},z_{2,2},z_{3,1},z_{3,2}\),只考虑 \(x,z_{1,1},z_{1,2},z_{2,1},z_{2,2},z_{3,1},z_{3,2}\) 的颜色,其他点的颜色已经确定,那么有 \(2^7=128\) 种颜色序列。而如果 \((z_{1,1},z_{1,2}),(z_{2,1},z_{2,2}),(z_{3,1},z_{3,2})\) 中每对点的颜色都一样,那么不管 \(x\) 的颜色是什么,操作一次后的颜色序列都一样。这说明这 \(128\) 种操作前的序列至多对应到 \(128-8=120\) 种可能的序列。所以感性理解就有 \(\frac{128-120}{128}=\frac{1}{16}\) 的序列是可以作为答案的。

这说明可以作为答案的颜色序列在 \(\{0,1\}^n\) 中的占比是不超过 \(O(1)\) 的,也就是期望随机不超过 \(O(1)\) 次就能得到答案,所以总复杂度是 \(O(n)\)

如果用占比为 \(\frac{1}{16}\) 计算,那么随机 \(300\) 次还找不到答案的概率不到 \(4\times10^{-9}\),即使是 \(10^6\) 组测试数据找不到答案的概率也只有不到 \(0.004\)

事实上,可以证明答案在所有序列中的占比要比 \(\frac{1}{16}\) 高得多,具体分析参考官方题解

通过代码:

#include <bits/stdc++.h>
typedef long long LL;
typedef std::pair<int, int> pii;
typedef unsigned long long ULL;
#define MP std::make_pair 
#define fi first
#define se second
char buf[1 << 20], *p1 = 0, *p2 = 0;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), (p1 == p2)) ? 0 : (*p1++))
int read()
{
    int s = 0; int f = 1, c = getchar();
    for (; !isdigit(c); c = getchar()) f ^= c == '-';
    for (; isdigit(c); c = getchar()) s = s * 10 + (c ^ 48);
    return f ? s : -s;
}
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fll;
template<typename T> void Fmax(T &x, T y){ if (x < y) x = y; }
template<typename T> void Fmin(T &x, T y){ if (y < x) x = y; }
const int MAXN = 100005;
int n, m, col[MAXN], id[MAXN][2];
int e[MAXN][3], cnt[MAXN];
void init()
{
    n = read(), m = n / 2 * 3; 
    memset(cnt + 1, 0, n << 2);
    for (int i = 1; i <= m; i++)
    {
        int u = read(), v = read();
        e[u][cnt[u]++] = v, e[v][cnt[v]++] = u;
    }
    for (int i = 1; i <= n; i++)
        id[i][0] = (i - 1) << 1, id[i][1] = (i - 1) << 1 | 1;
}
namespace SAT
{
    int hd[MAXN], to[MAXN * 3], nxt[MAXN * 3], tot;
    void add(int x, int y){ to[++tot] = y, nxt[tot] = hd[x], hd[x] = tot; }
    int dfn[MAXN], low[MAXN], dcnt, st[MAXN], tp, N, bel[MAXN];
    bool inSt[MAXN];
    void init(int n){ tot = -1, memset(hd, -1, n << 3); }
    void dfs(int x)
    {
        dfn[x] = low[x] = ++dcnt;
        inSt[st[++tp] = x] = true;
        for (int e = hd[x], y; ~e; e = nxt[e])
            if (!dfn[y = to[e]]) dfs(y), Fmin(low[x], low[y]);
            else if (inSt[y]) Fmin(low[x], dfn[y]);
        if (low[x] == dfn[x])
        {
            ++N;
            for (int u = -1; u != x; )
                inSt[u = st[tp--]] = false, bel[u] = N;
        }
    }
    bool tarjan(int n)
    {
        memset(dfn, 0, n << 3);
        dcnt = N = 0;
        for (int i = 0; i < n * 2; i++)
            if (!dfn[i]) dfs(i);
        for (int i = 0; i < n; i++)
            if (bel[i << 1] == bel[i << 1 | 1])
                return false;
        return true;
    }
}
bool chk()
{
    SAT::init(n);
    for (int i = 1; i <= n; i++)
    {
        int o = col[i];
        SAT::add(id[e[i][0]][o ^ 1], id[e[i][1]][o]), SAT::add(id[e[i][0]][o ^ 1], id[e[i][2]][o]);
        SAT::add(id[e[i][1]][o ^ 1], id[e[i][0]][o]), SAT::add(id[e[i][1]][o ^ 1], id[e[i][2]][o]);
        SAT::add(id[e[i][2]][o ^ 1], id[e[i][0]][o]), SAT::add(id[e[i][2]][o ^ 1], id[e[i][1]][o]);
    }
    return !SAT::tarjan(n);
}
std::mt19937 rnd(time(0));
void mian()
{
    init();
    for (; ;)
    {
        for (int i = 1; i <= n; i++)
            col[i] = rnd() & 1;
        if (chk())
        {
            for (int i = 1; i <= n; i++)
                putchar("BW"[col[i]]);
            putchar('\n'); return ;
        }
    }  
}
int main()
{
    for (int Tcnt = read(); Tcnt--; ) mian();
    return 0;
}
posted @ 2025-05-07 19:51  complexor  阅读(19)  评论(0)    收藏  举报