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的更多相关文章

  1. PAT 甲级 1154 Vertex Coloring

    https://pintia.cn/problem-sets/994805342720868352/problems/1071785301894295552 A proper vertex color ...

  2. pat甲级 1154 Vertex Coloring (25 分)

    A proper vertex coloring is a labeling of the graph's vertices with colors such that no two vertices ...

  3. PAT_A1154#Vertex Coloring

    Source: PAT A 1154 Vertex Coloring (25 分) Description: A proper vertex coloring is a labeling of the ...

  4. 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 ...

  5. PTA 1154 Vertex Coloring

    题目链接:1154 Vertex Coloring A proper vertex coloring is a labeling of the graph's vertices with colors ...

  6. PAT甲级——A1154 VertexColoring【25】

    A proper vertex coloring is a labeling of the graph's vertices with colors such that no two vertices ...

  7. django模型操作

    Django-Model操作数据库(增删改查.连表结构) 一.数据库操作 1.创建model表        

  8. 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 ...

  9. CF149D. Coloring Brackets[区间DP !]

    题意:给括号匹配涂色,红色蓝色或不涂,要求见原题,求方案数 区间DP 用栈先处理匹配 f[i][j][0/1/2][0/1/2]表示i到ji涂色和j涂色的方案数 l和r匹配的话,转移到(l+1,r-1 ...

  10. 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 ...

随机推荐

  1. new Date()在IOS中的坑(转)

    原文地址:https://www.cnblogs.com/zjjDaily/p/8963327.html 第一次遇到,记录一下 要创建一个指定时间的new Date对象时,通常的做法是: new Da ...

  2. curl: (35) SSL connect error的错误

    1.先升级nss (Network Security Service, 网络安全服务) yum update nss如果没有nss,则需要安装nss服务 yum install nss 2.更新/安装 ...

  3. button 样式 触发器

    <Style x:Key="Style.OkOperationButton" TargetType="ButtonBase"> <Setter ...

  4. 修改linux服务器时间

    date -s "20220104 11:07:05" &&hwclock --systohc

  5. 使用go的并发性来解决Hilbert酒店问题

    译自:Designing for Concurrency: the Hilbert's Hotel Problem in Go,本文使用go的并发性来解决Hilbert酒店问题.本文比较有意思的是它对 ...

  6. Python地理分析库whitebox在Anaconda中的配置

      本文介绍在Anaconda环境下,安装Python中的一个高级地理空间数据分析库whitebox的方法.   首先,我们打开"Anaconda Prompt (Anaconda)&quo ...

  7. webpack原理(1):Webpack热更新实现原理代码分析

    热更新,主要就是把前端工程 文件变更,即时编译,然后通知到浏览器端,刷新代码. 服务单与客户端通信方式有:ajax 轮询,EventSource.websockt. 客户端刷新一般分为两种: 整体页面 ...

  8. Go语言:利用 TDD 驱动开发测试 学习结构体、方法和接口

    环境安装: (新手向)在Linux中使用VScode编写 "Hello,world"程序,并编写测试-Ubuntu20.4 上一篇相关随笔: Go语言:利用 TDD 测试驱动开发帮 ...

  9. 关于MySQL建立库表时大写自动转换为小写的解决方案

    mysql 5.6以上windows对大小写敏感要在my.ini中的[mysqld]下面设置lower_case_table_names = 2 网上有的要改成0 亲测报错 [○・`Д´・ ○]

  10. Cannot read properties of undefined (reading 'toUpperCase')

    无法读取 JS 中未定义的属性"toUpperCase"|鲍比哈兹 (bobbyhadz.com) 根据其中的内容找到了答案:使用了未定义的属性去使用toUpperCase()函数 ...