ABC294Ex K-Coloring
Statement
对一张简单无向图进行 \(k\) 染色,满足对于每条边的两个端点颜色不同,求方案数。
\(n,m\leq 30\)。
Solution
无向图 \(k\) 染色问题,很经典的问题。
这道题的突破口是 \(n,m\) 均不大,所以 \(m-n\) 不会很大,这提示我们使用广义串并联图方法。
具体地,根据 EI 和洛谷讨论区里的说法,我们套路性地考虑对于每条边设 \(DP\),\(f_i\) 表示如果这条边两个端点被染了不同的颜色,这条边内部被缩略的结构中有多少种染色方案。\(g_i\) 表示如果这两条边端点被染了相同颜色的方案数。
那么对于原图中的边显然有 \(f_{u,v}=1\) 和 \(g_{u,v}=0\)。
广义串并联图方法的套路是对每条边设置 \(DP\) 后删一度点直接把贡献乘入答案,缩二度点和叠重边更新 \(DP\) 值。
下述推导来自讨论区:
对于删一度点,将答案乘上 \((k-1)f_u+g_u\),表示枚举删的这个点的颜色。
对于缩二度点,\(f_e=f_u f_v(k-2)+g_u f_v+f_u g_v,g_e=f_u f_v(k-1)+g_u g_v\),表示枚举中间那个点的颜色并分讨。
对于叠重边,\(f_e=f_u f_v,g_e=g_u g_v\),表示乘法原理。
现在图中的点满足了 \(n\leq \frac{2m}{3}\) 即 \(n\leq 20\)。
考虑对每一个颜色设一个集合幂级数,答案就是这些集合幂级数子集卷积的结果。
具体地,我们先假设所有边都取到了 \(f\) 的贡献,然后如果有一个颜色的集合包含了这条边的两个端点,就需要乘上一个 \(\frac{g}{f}\)。容易发现 \(f\) 总是非 \(0\) 的,所以一定存在逆元。这些贡献容易一遍 \(\text{FWT}\) 计算答案。
现在我们需要快速求集合幂级数 \(F\) 的 \(k\) 次方。然而 \(n\) 有 \(20\) 级别,所以需要 \(\ln\) 再 \(\exp\) 回去。复杂度是 \(O(2^n n^2)\) 的。
\(\ln,\exp\) 直接对占位幂级数 \(O(n^2)\) 求就可以了。
式子:
\(\ln:g_n=f_n-\frac{1}{n}\sum_{i=1}^{n-1} g_i i f_{n-i}\)。需要保证常数项为 \(1\)。
\(\exp:g_n=\frac{1}{n}\sum_{i=1}^{n} f_i i g_{n-i}\)。需要保证常数项为 \(0\)。
#include <cstdio>
using namespace std;
int read(){
char c=getchar();int x=0;
while(c<48||c>57) c=getchar();
do x=(x<<1)+(x<<3)+(c^48),c=getchar();
while(c>=48&&c<=57);
return x;
}
const int N=33,P=998244353;
typedef long long ll;
int qp(int a,int b=P-2){
int res=1;
while(b){
if(b&1) res=(ll)res*a%P;
a=(ll)a*a%P;b>>=1;
}
return res;
}
int n,m,k,res,cnt;
int f[N][N],g[N][N];
bool del[N];
int deg[N];
int F[1<<20];
int inv[21],id[N];
namespace Subset{
int n;
int f[21][1<<20];
int g[21][1<<20];
void inc(int &x,int v){if((x+=v)>=P) x-=P;}
void dec(int &x,int v){if((x-=v)<0) x+=P;}
void FWT(int *arr){
for(int i=1;i<(1<<n);i<<=1)
for(int j=0;j<(1<<n);j+=(i<<1))
for(int k=j;k<(j|i);++k) inc(arr[k|i],arr[k]);
}
void IFWT(int *arr){
for(int i=1;i<(1<<n);i<<=1)
for(int j=0;j<(1<<n);j+=(i<<1))
for(int k=j;k<(j|i);++k) dec(arr[k|i],arr[k]);
}
void getln(int *arr){
for(int i=0;i<=n;++i)
for(int s=0;s<(1<<n);++s) f[i][s]=g[i][s]=0;
for(int s=0;s<(1<<n);++s) f[__builtin_popcount(s)][s]=arr[s];
for(int i=0;i<=n;++i) FWT(f[i]);
for(int i=1;i<=n;++i){
for(int j=1;j<i;++j)
for(int s=0;s<(1<<n);++s)
dec(g[i][s],(ll)g[j][s]*j%P*f[i-j][s]%P);
for(int s=0;s<(1<<n);++s)
g[i][s]=((ll)g[i][s]*inv[i]+f[i][s])%P;
}
for(int i=0;i<=n;++i) IFWT(g[i]);
for(int s=0;s<(1<<n);++s) arr[s]=g[__builtin_popcount(s)][s];
}
void getexp(int *arr){
for(int i=0;i<=n;++i)
for(int s=0;s<(1<<n);++s) f[i][s]=g[i][s]=0;
for(int s=0;s<(1<<n);++s) f[__builtin_popcount(s)][s]=arr[s];
for(int i=0;i<=n;++i) FWT(f[i]);
for(int s=0;s<(1<<n);++s) g[0][s]=1;
for(int i=1;i<=n;++i){
for(int j=1;j<=i;++j)
for(int s=0;s<(1<<n);++s)
inc(g[i][s],(ll)f[j][s]*j%P*g[i-j][s]%P);
for(int s=0;s<(1<<n);++s)
g[i][s]=(ll)g[i][s]*inv[i]%P;
}
for(int i=0;i<=n;++i) IFWT(g[i]);
for(int s=0;s<(1<<n);++s) arr[s]=g[__builtin_popcount(s)][s];
}
}
int main(){
n=read();m=read();k=read();res=1;
for(int i=1;i<=m;++i){
int u=read(),v=read();
f[u][v]=f[v][u]=1;
++deg[u];++deg[v];
}
bool fl=1;
while(fl){
fl=0;
for(int u=1;u<=n;++u)
if(deg[u]==1){
fl=1;
del[u]=1;
for(int v=1;v<=n;++v)
if(f[u][v]){
--deg[u];--deg[v];
res=((ll)f[u][v]*(k-1)+g[u][v])%P*res%P;
f[u][v]=f[v][u]=0;
g[u][v]=g[v][u]=0;
}
break;
}
if(fl) continue;
for(int u=1;u<=n;++u)
if(deg[u]==2){
fl=1;
int x=0,y=0;
for(int v=1;v<=n;++v)
if(f[u][v]){if(x) y=v;else x=v;}
deg[u]=0;del[u]=1;
int ff=(ll)f[u][x]*f[u][y]%P;
int nf=((ll)ff*(k-2)+(ll)g[u][x]*f[u][y]+(ll)f[u][x]*g[u][y])%P;
int ng=((ll)ff*(k-1)+(ll)g[u][x]*g[u][y])%P;
f[u][x]=f[x][u]=f[u][y]=f[y][u]=0;
g[u][x]=g[x][u]=g[u][y]=g[y][u]=0;
if(!f[x][y]&&!f[y][x]){
f[x][y]=f[y][x]=nf;
g[x][y]=g[y][x]=ng;
}
else{
f[y][x]=f[x][y]=(ll)f[x][y]*nf%P;
g[y][x]=g[x][y]=(ll)g[x][y]*ng%P;
--deg[x];--deg[y];
}
break;
}
}
for(int i=1;i<=n;++i) if(!del[i]&&!deg[i]) res=(ll)res*k%P,del[i]=1;
for(int i=1;i<=n;++i)
if(!del[i]) id[i]=cnt++;
if(cnt){
inv[1]=1;Subset::n=cnt;
for(int i=2;i<=cnt;++i) inv[i]=(ll)inv[P%i]*(P-P/i)%P;
for(int i=0;i<(1<<cnt);++i) F[i]=1;
for(int i=1;i<=n;++i){
if(del[i]) continue;
for(int j=1;j<i;++j){
if(del[j]) continue;
if(f[i][j]){
res=(ll)res*f[i][j]%P;
int ver=(1<<id[i])|(1<<id[j]);
F[ver]=(ll)F[ver]*qp(f[i][j])%P*g[i][j]%P;
}
}
}
for(int i=1;i<(1<<cnt);i<<=1)
for(int j=0;j<(1<<cnt);j+=(i<<1))
for(int k=j;k<(j|i);++k) F[k|i]=(ll)F[k|i]*F[k]%P;
Subset::getln(F);
for(int i=0;i<(1<<cnt);++i) F[i]=(ll)F[i]*k%P;
Subset::getexp(F);
res=(ll)res*F[(1<<cnt)-1]%P;
}
printf("%d\n",res);
return 0;
}
ABC294Ex K-Coloring的更多相关文章
- PAT 甲级 1154 Vertex Coloring
https://pintia.cn/problem-sets/994805342720868352/problems/1071785301894295552 A proper vertex color ...
- pat甲级 1154 Vertex Coloring (25 分)
A proper vertex coloring is a labeling of the graph's vertices with colors such that no two vertices ...
- PAT_A1154#Vertex Coloring
Source: PAT A 1154 Vertex Coloring (25 分) Description: A proper vertex coloring is a labeling of the ...
- PAT Advanced 1154 Vertex Coloring (25 分)
A proper vertex coloring is a labeling of the graph's vertices with colors such that no two vertices ...
- PTA 1154 Vertex Coloring
题目链接:1154 Vertex Coloring A proper vertex coloring is a labeling of the graph's vertices with colors ...
- PAT甲级——A1154 VertexColoring【25】
A proper vertex coloring is a labeling of the graph's vertices with colors such that no two vertices ...
- django模型操作
Django-Model操作数据库(增删改查.连表结构) 一.数据库操作 1.创建model表
- Codeforces Round #369 (Div. 2)---C - Coloring Trees (很妙的DP题)
题目链接 http://codeforces.com/contest/711/problem/C Description ZS the Coder and Chris the Baboon has a ...
- CF149D. Coloring Brackets[区间DP !]
题意:给括号匹配涂色,红色蓝色或不涂,要求见原题,求方案数 区间DP 用栈先处理匹配 f[i][j][0/1/2][0/1/2]表示i到ji涂色和j涂色的方案数 l和r匹配的话,转移到(l+1,r-1 ...
- Codeforces Round #369 (Div. 2) C. Coloring Trees DP
C. Coloring Trees ZS the Coder and Chris the Baboon has arrived at Udayland! They walked in the pa ...
随机推荐
- new Date()在IOS中的坑(转)
原文地址:https://www.cnblogs.com/zjjDaily/p/8963327.html 第一次遇到,记录一下 要创建一个指定时间的new Date对象时,通常的做法是: new Da ...
- curl: (35) SSL connect error的错误
1.先升级nss (Network Security Service, 网络安全服务) yum update nss如果没有nss,则需要安装nss服务 yum install nss 2.更新/安装 ...
- button 样式 触发器
<Style x:Key="Style.OkOperationButton" TargetType="ButtonBase"> <Setter ...
- 修改linux服务器时间
date -s "20220104 11:07:05" &&hwclock --systohc
- 使用go的并发性来解决Hilbert酒店问题
译自:Designing for Concurrency: the Hilbert's Hotel Problem in Go,本文使用go的并发性来解决Hilbert酒店问题.本文比较有意思的是它对 ...
- Python地理分析库whitebox在Anaconda中的配置
本文介绍在Anaconda环境下,安装Python中的一个高级地理空间数据分析库whitebox的方法. 首先,我们打开"Anaconda Prompt (Anaconda)&quo ...
- webpack原理(1):Webpack热更新实现原理代码分析
热更新,主要就是把前端工程 文件变更,即时编译,然后通知到浏览器端,刷新代码. 服务单与客户端通信方式有:ajax 轮询,EventSource.websockt. 客户端刷新一般分为两种: 整体页面 ...
- Go语言:利用 TDD 驱动开发测试 学习结构体、方法和接口
环境安装: (新手向)在Linux中使用VScode编写 "Hello,world"程序,并编写测试-Ubuntu20.4 上一篇相关随笔: Go语言:利用 TDD 测试驱动开发帮 ...
- 关于MySQL建立库表时大写自动转换为小写的解决方案
mysql 5.6以上windows对大小写敏感要在my.ini中的[mysqld]下面设置lower_case_table_names = 2 网上有的要改成0 亲测报错 [○・`Д´・ ○]
- Cannot read properties of undefined (reading 'toUpperCase')
无法读取 JS 中未定义的属性"toUpperCase"|鲍比哈兹 (bobbyhadz.com) 根据其中的内容找到了答案:使用了未定义的属性去使用toUpperCase()函数 ...