【题目背景】
小S 是一个很喜欢图的女孩子。
【题目描述】
给出一个n 个点,m 条边的无向图,保证图是可爱的。
如果一个图是可爱的,那么它满足对于任意一个7 个点的集合S ,都存在两个点
a, b ∈ S 和一个点c < S ,使得任何一条a 到b 的路径都经过c 。
现在小S 想问你,对于1 ~ n 的每个数i ,将原图i 染色的方案数。
一个图的k 染色即为每个节点标注一个1 ~ k 的整数,一个染色方案合法即每条边
的两个端点颜色不同。
保证图中无重边自环,答案对998244353 取模。
【输入格式】
从文件blessing.in 中读入数据。
第一行包含两个正整数n,m
接下来的m 行,每行两个正整数a, b ,描述一条边。
【输出格式】
输出到文件blessing.out 中。
输出一行n 个整数,其中第i 个数表示i 染色的答案。
【样例1 输入】
6 6
1 3
2 4
1 4
2 5
2 6
3 4
【样例1 输出】
0 0 48 648 3840 15000
【样例2】
见选手目录下的blessing/blessing2.in 与blessing/blessing2.ans。
题解:
首先,我们可以发现题目的限制条件“可爱”等价于不存在大小超过6的联通块。
因为若存在,取相邻两点,再任意取五点就不满足。
同时本质不同(染色方案数完全不同)的联通块最多只有112种,所以可以把所有联通块的染色方案数暴力求解,用map套结构体维护联通块种类,最后再合并。类似hash思想。
OEIS A001349
学习了一下*操作。
一、结构体排序有两种方法
①
struct node{
int x,y;
bool operator<(const node&a)const{
return x<a.x;
}
}
②
struct node{
int x,y;
friend bool operator<(const node a,const node b){
return a.x<b.x;
}
}
而对于数组结构体进行排序也大同小异
struct node{
int a[7];
friend bool operator<(const node x,const node y){
for(int i=1;i<=6;++i){
if(x.a[i]<y.a[i])return true;
else if(x.a[i]>y.a[i])return false;
}
return false;
}
}
二、
若想要在调用结构体时直接复制(偷懒)
需要如下
struct node{
int x,y;
//node(){};若加上此行就不用赋初值为0
node(int a=0,int b=0){x=a,y=b};
}
对于数组需要
struct node{
int a[7];
node(){};
node(int _a[]){//不能用*_a,会报错
for(int i=1;i<=6;++i)a[i]=_a[i];
}
}
AC代码如下
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
#define N 100005
using namespace std;
const long long mod=998244353;
int tot,head[N],ver[N<<2],nex[N<<2],n,m,limit;
long long jc[N],jcinv[N];
inline long long q_pow(long long x,int c){
long long now=1;
while(c){
if(c&1)now=(now*x)%mod;
x=(x*x)%mod;c>>=1;
}
return now;
}
inline void add(int x,int y){
nex[++tot]=head[x];head[x]=tot;ver[tot]=y;
}
struct node{
long long a[7];
node(){};
node(long long _a[]){
for(int i=1;i<=6;++i)a[i]=_a[i];
}
long long calc(){
long long res=0;
for(int i=1;i<=min(limit,6);++i){
res=(res+jc[limit]*jcinv[limit-i]%mod*a[i])%mod;
}
return res;
}
bool operator <(const node &y)const{
for(int i=1;i<=6;++i){
if(a[i]<y.a[i])return true;
else if(a[i]>y.a[i])return false;
}
return false;
}
}final;
long long cnt[7];
int size,vis[N],color[N],pos[7];
void pd_liantong(int x){
pos[++size]=x;
vis[x]=1;
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(!vis[y]){
pd_liantong(y);
}
}
}
void dfs(int x,int tot){
if(x>size){
cnt[tot]++;return ;
}
int biao[7];
for(int i=1;i<=tot;++i)biao[i]=0;
for(int i=head[pos[x]];i;i=nex[i]){
int y=ver[i];
if(color[y])biao[color[y]]=1;
}
for(int i=1;i<=tot;++i){
if(!biao[i]){color[pos[x]]=i;dfs(x+1,tot);color[pos[x]]=0;}
}
color[pos[x]]=tot+1;
dfs(x+1,tot+1);
color[pos[x]]=0;
}
map<node, long long> sum;
map<node, long long>::iterator it;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=n;++i){
if(!vis[i]){
size=0;pd_liantong(i);
memset(cnt,0,sizeof(cnt));
dfs(1,0);
++sum[node(cnt)];
}
}
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i%mod;
jcinv[n]=q_pow(jc[n],mod-2);
for(int i=n;i;--i)jcinv[i-1]=jcinv[i]*i%mod;
for(int i=1;i<=n;++i){
long long ans=1;limit=i;
for(it=sum.begin();it!=sum.end();++it){
final=(*it).first;
ans=(ans*q_pow(final.calc(),(*it).second)%mod);
}
printf("%lld ",(ans+mod)%mod);
}
return 0;
}