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. mysql驱动下载

    下载地址:https://dev.mysql.com/downloads/connector/j/ 下载步骤:Select Operating System: Platform Independent

  2. ZooKeeper启动报错,未成功开启服务

    1.问题示例 (1)启动ZooKeeper服务报错 [Hadoop@master ~]$ zkServer.sh statusZooKeeper JMX enabled by defaultUsing ...

  3. go 发布

    rm test-serv.bingo build -o test-serv.bin main.go

  4. java8-并行计算

    java8提供一个fork/join framework,fork/join框架是ExecutorService接口的一个实现,它可以帮助你充分利用你电脑中的多核处理器,它的设计理念是将一个任务分割成 ...

  5. Java-http请求工具-OkHttp用法

    前言:一般Java项目后端发送请求都使用http,最近项目里面大佬建议把http都改成okhttp3(OkHttpClient).故今日记录部分常用发送方式. 代码:为了便于以后使用,这里封装一个Ok ...

  6. SimpleITK和nibable读取nii文件

    import SimpleITK as sitk path = 'nii全路径' nii_obj = sitk.GetArrayFromImage(sitk.ReadImage(path)) #sha ...

  7. python pandas库总结-数据分析和操作工具

    参考:https://pandas.pydata.org/ Input/output相关函数 pandas.read_excel-将Excel文件读入pandas数据框 支持读取xls, xlsx, ...

  8. @Valid 注解类型参数校验

    <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api& ...

  9. SpringBoot笔记--自动配置(高级内容)(上集)

    原理分析 自动配置 Condition--增加的条件判断功能 来一个案例说明: 具体实现: 没有要求的话,就是这样的: Config.java User.java SpringLearnApplica ...

  10. LoadRunner——创建场景及运行场景(三)

    1. 创建场景 场景:用户并发访问的策略,包含用户数.加载用户的时间等 启动第二个组件 : 控制台 (controller) 方式1: 开始->所有程序->HPLoadRunner-> ...