题目:
分析:
人丧病脑子不好使
不管这个图怎么建的,我们先考虑这种图怎么染色
一般图染色不会做,我们看看这个图的一些性质
考虑从后往前染色,一个点的颜色会被后面的与他有连边的点限制
而根据题目的构图方法,一个点后面与他有连边的点一定会构成一个团,两两颜色一定不同
把图改为有向,小的向大的连边,一个点的出度设为(d)
答案就是(prod_{i=1}^{n}(n-d_i))
难受,考试时我从前往后想没想出来,没想到从后往前
人丧病脑子不好使
于是我们如果知道了每一个点的出度,可以(O(n))算答案
从前往后加入边
一个点(u)加入的边,其所连的点的点集({v})一定成为一个团互相有连边
换一个角度,每一个所连的点(v)都会继承(u)的点集,与(v)自身所连的点的点集合并,只不过要删除不大于(v)的点
变成了集合合并。。。
直接上set,启发式合并
复杂度(O(nlog^{2}n)),跑得过(1e6)还真离谱
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<iostream>
#include<map>
#include<set>
#include<bitset>
#include<string>
#include<deque>
#define maxn 1000005
#define INF 0x3f3f3f3f
#define MOD 998244353
using namespace std;
inline long long getint()
{
long long num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,m;
vector<int>G[maxn];
set<int>S[maxn];
int ans=1;
inline void merge(int x,int y)
{
if(S[x].size()<S[y].size())swap(S[x],S[y]);
for(set<int>::iterator it=S[y].begin();it!=S[y].end();it++)S[x].insert(*it);
S[y].clear();
}
int main()
{
n=getint(),m=getint();
for(int i=1;i<=m;i++)
{
int u=getint(),v=getint();
if(u>v)swap(u,v);
G[u].push_back(v);
}
for(int i=1;i<=n;i++)S[i].insert(i);
for(int i=1;i<=n;i++)
{
for(int j=0;j<G[i].size();j++)S[i].insert(G[i][j]);
S[i].erase(i);
ans=1ll*ans*(n-S[i].size())%MOD;
if(!S[i].empty())merge(*(S[i].begin()),i);
}
printf("%d
",ans);
}