祝福(blessing)

【题目背景】
小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;
}
			
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值