[ABC294Ex] K-Coloring
考虑 dfs 后搞出 dfs 树,考虑若干返祖边有限制,那么,我们一个朴素的想法是枚举这些有被返祖边搞到的点的颜色,但这样最坏是 \(O(K^n)\) 的。
但显然一条返祖边在钦定完一个端点的颜色之后,另一端的颜色便可以不用钦定,因此指数可以减小一点。
那么我们可以钦定每个返祖边深度小的点的颜色,但是直接钦定是 \(O(K^t)\) 的,\(t\) 是返祖边深度小的点的集合大小。考虑我们并不关心其真正的颜色是什么,因此这东西可以集合划分后再给每个集合分配颜色。
那么这样子,你就有若干点的颜色钦定了,记钦定的颜色数量为 \(c\),但整棵树的染色显然不可能只用 \(c\) 种,但其他的颜色我们都是未钦定的,考虑另分一类给这些颜色。
记 \(f(x,i)\) 表示 \(x\) 为第 \(i\) 种颜色的时候,其子树内染色的方案数。
考虑转移,对于树边的转移是朴素的。
对于返祖边的话,由于我们已经钦定了深度小的点的颜色,那么当前点的颜色也必须与其相同,不相同的方案数直接没了即可。
注意之后方案数应乘上对该集合划分分配颜色的方案数。
不重不漏的证明:考虑一种合法的染色方案,显然会被在钦定其返祖点的颜色时钦定到这种方案,且只钦定到一次。假如一种方案被钦定了两次的话。若颜色划分的集合不同,那么两种染色方案一定不同。若颜色划分的集合相同,但对集合分配的颜色方案不同,则两种染色方案一定不同。则关键,在于转移。而我们的转移并不会重算同一种方案,因为其都是子树各自考虑而又独立合并的。所以一种方案不会被钦定多次。
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int mod=998244353;
struct edge {
int nex,to;
}e[70];
int f[35][35],n,m,K,dfn[35],tot,otot,o[35],rt,col[35],fa[35],hea[35],cnt=1;
void add_edge(int x,int y) {
e[++cnt].nex=hea[x]; e[cnt].to=y; hea[x]=cnt;
}
void dfs0(int x,int pre) {
dfn[x]=++tot;
for(int i=hea[x];i;i=e[i].nex) {
if((i==(pre^1))) continue ;
int y=e[i].to;
if(!dfn[y]) dfs0(y,i);
else if(dfn[y]<=dfn[x]) {
o[++otot]=y;
}
}
}
bool vis[35];
int C;
void dp(int x,int pre) {
if(!col[x]) {
for(int i=0;i<=C;i++) f[x][i]=1;
} else {
for(int i=0;i<=C;i++) f[x][i]=0;
f[x][col[x]]=1;
}
vis[x]=1;
// cout<<x<<" "<<ff<<" in\n";
for(int j=hea[x];j;j=e[j].nex) {
if((j==(pre^1))) continue ;
int y=e[j].to;
if(!vis[y]) {
dp(y,j);
int qwq=f[y][0]*(K-C-1)%mod;
for(int i=1;i<=C;i++) qwq=(qwq+f[y][i])%mod;
f[x][0]=f[x][0]*qwq%mod;
qwq=0;
for(int i=1;i<=C;i++) qwq=(qwq+f[y][i])%mod;
for(int i=1;i<=C;i++) {
int qaq=(f[y][0]*(K-C)%mod+(qwq-f[y][i]+mod)%mod)%mod;
f[x][i]=f[x][i]*qaq%mod;
}
} else if(dfn[y]<=dfn[x]) {
for(int i=0;i<=C;i++) {
if(i==col[y]) f[x][i]=0;
}
}
}
// cout<<": "<<x<<" -> "<<col[x]<<'\n';
// for(int i=0;i<=C;i++) cout<<f[x][i]<<' ';
// cout<<'\n';
}
int nwans,ans;
void dfs(int x,int c) {
if(c>K) return ;
if(x==otot+1) {
for(int i=1;i<=n;i++) vis[i]=0;
C=c; dp(rt,0);
int res=f[rt][0]*(K-C)%mod;
for(int i=1;i<=C;i++) res=(res+f[rt][i])%mod;
for(int i=K-C+1;i<=K;i++) res=res*i%mod;
nwans=(nwans+res)%mod;
return ;
}
for(int i=1;i<=c;i++) col[o[x]]=i,dfs(x+1,c);
col[o[x]]=c+1; dfs(x+1,c+1);
}
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>n>>m>>K;
for(int i=1;i<=m;i++) {
int x,y; cin>>x>>y; add_edge(x,y); add_edge(y,x);
}
ans=1;
for(int i=1;i<=n;i++) {
if(!dfn[i]) {
rt=i; nwans=0; otot=0;
dfs0(rt,0);
sort(o+1,o+1+otot);
if(otot) otot=unique(o+1,o+1+otot)-o-1;
// for(int j=1;j<=otot;j++) cout<<o[j]<<' ';
// cout<<'\n';
dfs(1,0);
// cout<<nwans<<'\n';
ans=ans*nwans%mod;
}
}
ans=(ans%mod+mod)%mod; cout<<ans;
return 0;
}