其 实 很 好 写 其实很好写 其实很好写

首 先 因 为 要 分 三 组 , 那 我 们 就 先 分 三 组 , 保 证 组 内 点 都 是 互 相 不 连 通 的 首先因为要分三组,那我们就先分三组,保证组内点都是互相不连通的 首先因为要分三组,那我们就先分三组,保证组内点都是互相不连通的

所 以 先 枚 举 1 个 点 , 把 这 个 点 所 有 不 可 达 的 点 标 记 并 记 作 集 合 1 所以先枚举1个点,把这个点所有不可达的点标记并记作集合1 所以先枚举1个点,把这个点所有不可达的点标记并记作集合1

再 用 相 同 方 式 构 造 集 合 2 , 3 再用相同方式构造集合2,3 再用相同方式构造集合2,3

此 时 如 果 三 个 集 合 没 有 把 所 有 点 包 含 进 来 说 明 不 可 行 此时如果三个集合没有把所有点包含进来说明不可行 此时如果三个集合没有把所有点包含进来说明不可行

假 如 有 答 案 , 我 们 这 样 构 造 一 定 是 对 的 假如有答案,我们这样构造一定是对的 假如有答案,我们这样构造一定是对的

接下来是重点,如何验证每个点集可行?

首 先 如 果 三 个 点 集 分 别 由 q , w , e 个 点 首先如果三个点集分别由q,w,e个点 首先如果三个点集分别由q,w,e个点

那 么 q ∗ w + q ∗ e + w ∗ e = = m 那么q*w+q*e+w*e==m 那么q∗w+q∗e+w∗e==m

但 是 光 是 这 一 条 还 不 够 但是光是这一条还不够 但是光是这一条还不够

我 们 去 d f s , 集 合 为 1 的 点 只 能 去 集 合 为 2 , 3 的 点 , 根 据 这 个 判 断 集 合 是 否 可 行 我们去dfs,集合为1的点只能去集合为2,3的点,根据这个判断集合是否可行 我们去dfs,集合为1的点只能去集合为2,3的点,根据这个判断集合是否可行

虽 然 上 面 的 步 骤 仍 然 会 有 漏 网 之 鱼 , 但 是 这 种 数 据 还 是 比 较 难 构 造 的 虽然上面的步骤仍然会有漏网之鱼,但是这种数据还是比较难构造的 虽然上面的步骤仍然会有漏网之鱼,但是这种数据还是比较难构造的

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
vector<int>vec[maxn];
int n,m,flag=1,vis[maxn],a[maxn],duo;
set<int>s[maxn];
void dfs(int u)
{
duo++;vis[u]=1;
if(flag==0) return;
for(int i=0;i<vec[u].size();i++)
{
int v=vec[u][i];
if(a[v]==a[u]) flag=0;
if(vis[v]) continue;
if(flag==0) return ;
if(a[v]!=a[u]) dfs(v);
}
}
void over(){
cout<<"-1";
exit(0);
}
int main()
{
cin >> n >> m;
for(int i=1,l,r;i<=m;i++)
{
scanf("%d%d",&l,&r);
s[l].insert(r);
s[r].insert(l);
vec[l].push_back(r);
vec[r].push_back(l);
}
int k=0;
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
k++;
if(k>3) over();
vis[i]=1,a[i]=k;
for(int j=1;j<=n;j++)
{
if(i==j) continue;
if( s[i].count(j) ) continue;
a[j]=k; vis[j]=1;
}
}
if(k!=3) over();
memset(vis,0,sizeof(vis));

int q=0,w=0,e=0;
for(int i=1;i<=n;i++)
if(a[i]==1) q++;
else if(a[i]==2) w++;
else e++;

for(int i=n;i>=1;i--)
if(vis[i]==0) dfs(i);

if(flag==0||duo!=n||q*w+q*e+w*e!=m) over();
else
{
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
}
}