嘘~ 正在从服务器偷取页面 . . .

LOJ2406 「THUPC 2017」母亲节的礼物 / Gift


LOJ2406 「THUPC 2017」母亲节的礼物 / Gift

题目链接:#2406. 「THUPC 2017」母亲节的礼物 / Gift

题意

cxy 喜欢图,尤其是边数不太多的无向简单图。母亲节快到了,cxy 在纸上画了一张有 \(n\) 个节点、\(m\) 条边的无向简单图(即不存在重边、自环),保证每个点只和最多 \(7\) 个点相邻。接着,他想用 \(4\) 种不同的颜色给图中的节点进行染色,作为妈妈的母亲节礼物送给她。

cxy 希望染色之后的图尽量漂亮,他觉得相同颜色的点连成一片不好看。所以,他希望能给每对相邻的节点染上不同的颜色。遗憾的是,cxy 很快发现,在有些图中,这是不可能做到的。他不得不降低要求:每个点相邻的点中,至多有一个点和它的颜色相同。

限制条件放松了,问题也就变得简单了;但是 cxy 忙着做大作业,所以来找你帮忙。现在,请你告诉 cxy,是否能给图中每个点染上一个恰当的颜色,恰好满足 cxy 的要求?如果可以,请你给他指出一种染色方案;否则,只好残忍地告诉 cxy:impossible

输入格式

输入有多组数据,第一行一个整数 \(T(1\leq T\leq 10)\),表示数据组数。

对于每组数据:

第一行两个整数 \(n,m(1\leq n\leq 25000,1\leq m\leq 100000)\),分别表示图的点数和边数(约定点从 \(1\) 开始标号)。

接下来 \(m\) 行,每行两个整数 \(x,y(1\leq x,y\leq n)\),描述图中的一条边,保证不存在重边、自环。

保证在一个输入文件中,有 \(\sum n\leq 200000,\sum m\leq 800000\)

输出格式

我们用小写英文字母 \(\mathtt{abcd}\) 给四种不同的颜色标号。

对于每组数据:

  • 如果有解,输出一行一个长度为 \(n\) 的字符串 \(S\),其中 \(S_i\) 表示你给第 \(i\) 个点染上的颜色(下标从 \(1\) 开始);
  • 否则,输出 impossible

特殊性质是度数不超过 \(7\) ,而颜色只有 \(4\) 种。

则与节点 \(u\) 相邻的点中至少有一个点的颜色只出现了不超过一次,此时我们只需要另 \(u\) 为该颜色即可使其合法。

但是这可能导致其他的节点不符合条件,因此考虑递归处理这些点,于是我们可以得到一个这样的算法:

  1. 将原图随机染色为 \(G\) ,清空队列 \(q\)
  2. 找到 \(G\) 中的所有不合法节点 \(u\) ,即与 \(u\) 相邻的点中,至少 \(2\) 个点与 \(u\) 同色。
  3. \(q = \varnothing\) ,则转到步骤 6,否则弹出队首 \(u\) 。若 \(u\) 已经是合法节点,重复步骤 3。
  4. 否则 \(u\) 的零点中必存在一种颜色 \(c_0\) ,其出现次数不超过 \(1\) 次。令 \(c(u) \gets c_0\)
  5. 遍历其邻点,若发现合法点变为不合法点,则将其加入队列 \(q\) ,并返回步骤 \(3\)
  6. 此时的图 \(G^{\prime}\) 已是原图的一个合法 \(4-\!\) 染色 。

分析一下时间复杂度。考虑每次修改的增量。设 \(K(G)\)\(G\) 中同色边的数量

设点 \(u\) 的邻点为 \(c_1,c_2,\cdots\) ,设 \(k(k \ge 2)\) 个点与 \(u\) 的颜色相同,而更改后,邻点中至多不超过 \(1\) 个与 \(u\) 同色

则每轮的增量为 \(\Delta K(G) \le 1 - k \le -1\) ,而初始 \(K(G) \le m\) ,最终的 \(K(G) \ge 0\) ,则操作数为 \(\mathcal{O}(m)\)

因此时间复杂度为 \(\mathcal{O}(n+m\Delta(G))\) ,其中 \(\Delta(G)\)\(G\) 的最大度,数值上等于 \(7\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))

mt19937 rd(time(0));
int n,m,deg[N]; char col[N],inq[N];
vector<int> vec[N]; queue<int> q;
void clear()
{
    for(int i = 1; i <= n; i++) {
        vec[i].clear(); vec[i].shrink_to_fit(); deg[i] = col[i] = 0;
    }
}
void Modify(int u,int c)
{
    int old = col[u]; col[u] = c; deg[u] = 0;
    for(auto v : vec[u]) {
        if(col[v] == old) --deg[v];
        if(col[v] == c && (++deg[u], ++deg[v] >= 2) && !inq[v]) {
            q.push(v); inq[v] = 1;
        }
    }
}
void solve()
{
    cin >> n >> m; clear(); 
    for(int i = 1, u,v; i <= m; i++) {
        cin >> u >> v;
        vec[u].emplace_back(v); vec[v].emplace_back(u);
    }
    for(int i = 1; i <= n; i++) col[i] = rd() & 3;
    for(int u = 1; u <= n; u++)
    {
        for(auto v : vec[u]) deg[u] += (col[u] == col[v]);
        if(deg[u] >= 2) { q.push(u); inq[u] = 1; }
    }
    for(int u,cur; !q.empty(); )
    {
        u = q.front(); q.pop(); inq[u] = cur = 0; if(deg[u] < 2) continue;
        for(auto v : vec[u]) cur += (1 << (col[v] * 3));
        for(int i = 0; i < 4; i++) if(!(cur >> (i * 3 + 1) & 3)) { Modify(u,i); break; }
    }
    for(int i = 1; i <= n; i++) { col[i] += 'a'; } col[n + 1] = 0; cout << (col + 1) << '\n';
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int Q; cin >> Q; while(Q--) solve();
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/?redirect=467


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录