联考20200608 T2 图

题目:


分析:
人丧病脑子不好使
不管这个图怎么建的,我们先考虑这种图怎么染色
一般图染色不会做,我们看看这个图的一些性质
考虑从后往前染色,一个点的颜色会被后面的与他有连边的点限制
而根据题目的构图方法,一个点后面与他有连边的点一定会构成一个团,两两颜色一定不同
把图改为有向,小的向大的连边,一个点的出度设为\(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\n",ans);
}

posted @ 2020-06-10 11:55  Izayoi_Doyo  阅读(153)  评论(0编辑  收藏  举报