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\)。

  1. #include <cstdio>
  2. using namespace std;
  3. int read(){
  4. char c=getchar();int x=0;
  5. while(c<48||c>57) c=getchar();
  6. do x=(x<<1)+(x<<3)+(c^48),c=getchar();
  7. while(c>=48&&c<=57);
  8. return x;
  9. }
  10. const int N=33,P=998244353;
  11. typedef long long ll;
  12. int qp(int a,int b=P-2){
  13. int res=1;
  14. while(b){
  15. if(b&1) res=(ll)res*a%P;
  16. a=(ll)a*a%P;b>>=1;
  17. }
  18. return res;
  19. }
  20. int n,m,k,res,cnt;
  21. int f[N][N],g[N][N];
  22. bool del[N];
  23. int deg[N];
  24. int F[1<<20];
  25. int inv[21],id[N];
  26. namespace Subset{
  27. int n;
  28. int f[21][1<<20];
  29. int g[21][1<<20];
  30. void inc(int &x,int v){if((x+=v)>=P) x-=P;}
  31. void dec(int &x,int v){if((x-=v)<0) x+=P;}
  32. void FWT(int *arr){
  33. for(int i=1;i<(1<<n);i<<=1)
  34. for(int j=0;j<(1<<n);j+=(i<<1))
  35. for(int k=j;k<(j|i);++k) inc(arr[k|i],arr[k]);
  36. }
  37. void IFWT(int *arr){
  38. for(int i=1;i<(1<<n);i<<=1)
  39. for(int j=0;j<(1<<n);j+=(i<<1))
  40. for(int k=j;k<(j|i);++k) dec(arr[k|i],arr[k]);
  41. }
  42. void getln(int *arr){
  43. for(int i=0;i<=n;++i)
  44. for(int s=0;s<(1<<n);++s) f[i][s]=g[i][s]=0;
  45. for(int s=0;s<(1<<n);++s) f[__builtin_popcount(s)][s]=arr[s];
  46. for(int i=0;i<=n;++i) FWT(f[i]);
  47. for(int i=1;i<=n;++i){
  48. for(int j=1;j<i;++j)
  49. for(int s=0;s<(1<<n);++s)
  50. dec(g[i][s],(ll)g[j][s]*j%P*f[i-j][s]%P);
  51. for(int s=0;s<(1<<n);++s)
  52. g[i][s]=((ll)g[i][s]*inv[i]+f[i][s])%P;
  53. }
  54. for(int i=0;i<=n;++i) IFWT(g[i]);
  55. for(int s=0;s<(1<<n);++s) arr[s]=g[__builtin_popcount(s)][s];
  56. }
  57. void getexp(int *arr){
  58. for(int i=0;i<=n;++i)
  59. for(int s=0;s<(1<<n);++s) f[i][s]=g[i][s]=0;
  60. for(int s=0;s<(1<<n);++s) f[__builtin_popcount(s)][s]=arr[s];
  61. for(int i=0;i<=n;++i) FWT(f[i]);
  62. for(int s=0;s<(1<<n);++s) g[0][s]=1;
  63. for(int i=1;i<=n;++i){
  64. for(int j=1;j<=i;++j)
  65. for(int s=0;s<(1<<n);++s)
  66. inc(g[i][s],(ll)f[j][s]*j%P*g[i-j][s]%P);
  67. for(int s=0;s<(1<<n);++s)
  68. g[i][s]=(ll)g[i][s]*inv[i]%P;
  69. }
  70. for(int i=0;i<=n;++i) IFWT(g[i]);
  71. for(int s=0;s<(1<<n);++s) arr[s]=g[__builtin_popcount(s)][s];
  72. }
  73. }
  74. int main(){
  75. n=read();m=read();k=read();res=1;
  76. for(int i=1;i<=m;++i){
  77. int u=read(),v=read();
  78. f[u][v]=f[v][u]=1;
  79. ++deg[u];++deg[v];
  80. }
  81. bool fl=1;
  82. while(fl){
  83. fl=0;
  84. for(int u=1;u<=n;++u)
  85. if(deg[u]==1){
  86. fl=1;
  87. del[u]=1;
  88. for(int v=1;v<=n;++v)
  89. if(f[u][v]){
  90. --deg[u];--deg[v];
  91. res=((ll)f[u][v]*(k-1)+g[u][v])%P*res%P;
  92. f[u][v]=f[v][u]=0;
  93. g[u][v]=g[v][u]=0;
  94. }
  95. break;
  96. }
  97. if(fl) continue;
  98. for(int u=1;u<=n;++u)
  99. if(deg[u]==2){
  100. fl=1;
  101. int x=0,y=0;
  102. for(int v=1;v<=n;++v)
  103. if(f[u][v]){if(x) y=v;else x=v;}
  104. deg[u]=0;del[u]=1;
  105. int ff=(ll)f[u][x]*f[u][y]%P;
  106. int nf=((ll)ff*(k-2)+(ll)g[u][x]*f[u][y]+(ll)f[u][x]*g[u][y])%P;
  107. int ng=((ll)ff*(k-1)+(ll)g[u][x]*g[u][y])%P;
  108. f[u][x]=f[x][u]=f[u][y]=f[y][u]=0;
  109. g[u][x]=g[x][u]=g[u][y]=g[y][u]=0;
  110. if(!f[x][y]&&!f[y][x]){
  111. f[x][y]=f[y][x]=nf;
  112. g[x][y]=g[y][x]=ng;
  113. }
  114. else{
  115. f[y][x]=f[x][y]=(ll)f[x][y]*nf%P;
  116. g[y][x]=g[x][y]=(ll)g[x][y]*ng%P;
  117. --deg[x];--deg[y];
  118. }
  119. break;
  120. }
  121. }
  122. for(int i=1;i<=n;++i) if(!del[i]&&!deg[i]) res=(ll)res*k%P,del[i]=1;
  123. for(int i=1;i<=n;++i)
  124. if(!del[i]) id[i]=cnt++;
  125. if(cnt){
  126. inv[1]=1;Subset::n=cnt;
  127. for(int i=2;i<=cnt;++i) inv[i]=(ll)inv[P%i]*(P-P/i)%P;
  128. for(int i=0;i<(1<<cnt);++i) F[i]=1;
  129. for(int i=1;i<=n;++i){
  130. if(del[i]) continue;
  131. for(int j=1;j<i;++j){
  132. if(del[j]) continue;
  133. if(f[i][j]){
  134. res=(ll)res*f[i][j]%P;
  135. int ver=(1<<id[i])|(1<<id[j]);
  136. F[ver]=(ll)F[ver]*qp(f[i][j])%P*g[i][j]%P;
  137. }
  138. }
  139. }
  140. for(int i=1;i<(1<<cnt);i<<=1)
  141. for(int j=0;j<(1<<cnt);j+=(i<<1))
  142. for(int k=j;k<(j|i);++k) F[k|i]=(ll)F[k|i]*F[k]%P;
  143. Subset::getln(F);
  144. for(int i=0;i<(1<<cnt);++i) F[i]=(ll)F[i]*k%P;
  145. Subset::getexp(F);
  146. res=(ll)res*F[(1<<cnt)-1]%P;
  147. }
  148. printf("%d\n",res);
  149. return 0;
  150. }

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. Hadoop服务启动失败

    Hadoop服务启动失败今天启动Hadoop时,终端报了一个新的错误 ```bashStarting namenodes on [192.168.19.128]192.168.19.128: ssh: ...

  2. 《MySQL是怎样运行的》第五章小结

  3. Java笔记第八弹

    设置和获取线程名称 //方法 void setName(String name);//将此线程的名称更改为等于参数name String getName();//返回此线程的名称 public sta ...

  4. ngix安装与使用

    主要是nginx的安装使用, 至于原理 1. 安装nginx(以及两个tomcat) 2. 使用nginx(测试负载均衡) 想要搭建的测试环境, 1.两个tomcat, 端口分别是80和8090(因为 ...

  5. 在昇腾平台上对TensorFlow网络进行性能调优

    摘要:本文就带大家了解在昇腾平台上对TensorFlow训练网络进行性能调优的常用手段. 本文分享自华为云社区<在昇腾平台上对TensorFlow网络进行性能调优>,作者:昇腾CANN . ...

  6. 基于机器学习的语音编解码器声网Agora Silver:支持超低码率下的高音质语音互动

    从 1860 年电话发明,到现如今通过网络进行语音互动,语音始终是最自然.最基础的实时互动方式.过去几年,语音实时互动成为越来越多人日常生活的一部分.但是每个人都会遇到弱网环境,这会直接影响语音通话体 ...

  7. 声网把七年无全网事故的实时传输网络SD-RTN全面开放了——这就是FPA!

    8 月 19 日,声网Agora 举办线上产品发布会,正式发布了"全链路加速 FPA(Full-Path Accelerator)".全链路加速 FPA 基于声网的软件定义实时网络 ...

  8. Python——基础知识(一)

    1. 那么多编程语言,为什么学python 易于学习,是所有编程语言当中最容易学习的 没有最好的语言,只有最合适的语言 2. 反复执行的用例如何提升效率 测试流程回归(回顾) 很多测试用例在不同的测试 ...

  9. Synchronized和Volatile的对比

    Synchronized和Volatile是并发中的两大关键字,有相似性和不同点. Synchronized更详细介绍参考https://www.cnblogs.com/spark-cc/p/1706 ...

  10. SHA-256 简介及 C# 和 js 实现【加密知多少系列】

    〇.简介 SHA-256 是 SHA-2 下细分出的一种算法.截止目前(2023-03)未出现"碰撞"案例,被视为是绝对安全的加密算法之一. SHA-2(安全散列算法 2:Secu ...