贪心

两个小时没做出来,首要原因居然不是我菜,而是寝室里有 shaber 碍手碍脚。

赛时猜测有解条件为 $m \leq 2n - 1$,实际上这是对的,赛时我写了个暴力搜索,可惜没顶什么用。对于给定的 $n$ 只要构造出 $m = 2n - 1$ 的方案就可以完成构造。

进行一些转化。

没有同色环说明如果仅保留一种颜色的边,二分图将会变成森林。共有 $2n+m$ 个节点,所以每种颜色的边至多有 $2n + m - 1$ 条。总边数为 $2nm$,颜色有 $n$ 种,据此列出不等式。

$$(2n + m - 1) n \geq 2nm$$

$$2n + m - 1 \geq 2m$$

$$2n - 1 \geq m$$

因此当 $m \leq 2n - 1$ 时才可能有解,这个条件是充要的。条件是赛时猜的,题解里也没证明为啥,但是能给出对应构造方案就说明它是对的。

考虑利用二分图这个条件,只要考虑左部点或考虑右部点的连边即可完成整张图的连边

对于每种颜色,分出 $2$ 条边给每个右部点。这一步是直观但富有人类智慧的

那么被某个右部点的同色边连接的两个左部点属于同一连通块,这等价于一种“连边”。对于某一个颜色,在 $m = 2n - 1$ 次连边之后 $2n$ 个左部点将属于同一连通块,也就是成为一棵树。

最直接的手段是将左部点通过“连边”变成链。

这需要让左部点的第 $i$ 个节点和第 $i+1$ 个节点连边,所以对于一种颜色,有恰好 $m = 2n-1$ 条边要连。

将第一种颜色对应的边 $(1,2),(2,3),(3,4),\cdots,(2n-1,2n)$ 依次分给第 $1 \sim m$ 个右部点,完成第 $1$ 种颜色的连边。接下来进行第 $1$ 次循环同构,将第二种颜色对应的边 $(3,4),(4,5),(5,6),\cdots,(1,2),(2,3)$ 依次分给第 $1 \sim m$ 个右部点,完成第 $2$ 种颜色的连边。以此类推直到完成第 $n-1$ 种颜色的连边。

下面展示一个 $n = 4,m = 7$ 的实例。

$$(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)(7,8)$$

$$(3,4)(4,5)(5,6)(6,7)(7,8)(1,2)(2,3)$$

$$(5,6)(6,7)(7,8)(1,2)(2,3)(3,4)(4,5)$$

继续循环同构试图获得第 $n$ 种颜色对应的边序列将会得到下面的边序列。容易发现添加这个序列后构造的图不符合题目要求。

$$(7,8)(1,2)(2,3)(3,4)(4,5)(5,6)(6,7)$$

解决方法很简单,由于 $n-1$ 个颜色的连边已经完成,每个节点都只剩下两条边没有连,找到这两条剩余的边并将它们的颜色设为 $n$ 即可。

时间复杂度 $O(n^2)$。

#include<bits/stdc++.h>
using namespace std;
int n,m,ans[2'005][2'005];
void solution(){
    cin>>n>>m;
    if(n*2-1<m) return cout<<"No"<<'\n',void();
    for(int i=1;i<=n*2;i++)
        for(int j=1;j<=n*2-1;j++) ans[i][j]=0;
    list<pair<int,int>> s;
    for(int i=1;i<=n*2-1;i++) s.push_back({i,i+1});
    for(int i=1;i<=n-1;i++){
        int pos=1;
        for(auto [x,y]:s){
            ans[x][pos]=i;
            ans[y][pos]=i;
            ++pos;
        }
        s.push_back(s.front());
        s.pop_front();
        s.push_back(s.front());
        s.pop_front();
    }
    cout<<"Yes"<<'\n';
    for(int i=1;i<=n*2;i++){
        for(int j=1;j<=m;j++){
            if(ans[i][j]) cout<<ans[i][j]<<' ';
            else cout<<n<<' ';
        }
        cout<<'\n';
    }
}
int T;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>T;
    while(T--) solution();
    return 0;
}
最后修改:2024 年 12 月 30 日
如果觉得我的文章对你有用,请随意赞赏