【题解】QOJ-7412 Counting Cactus
之前没做过仙人掌,甚至没学过圆方树,理解了半天改出来了,遂记。
尝试用理解树的方法理解一个仙人掌,钦定根为 ,那么一个与 有关的子仙人掌(类比子树)有如下两种情况:
-
一个 所在的环,环中除了 以外的节点挂任意仙人掌,套在根上。
-
一个 引出的边 ,以 为根的一个仙人掌。

结合上图, 号节点构成的子仙人掌就是以环的形式套在根上,而另一个子仙人掌则是由 引出的,属于第二种情况。
最基本的状态设 为点集 中以 为根的仙人掌个数。
根据上面对子仙人掌的讨论,我们知道第一种情况要求 的连边恰好有一个环,需要另设状态 表示(具体定义同 );而第二种情况只要枚举连边再增加一个 即可。
思考符合 定义的仙人掌怎么得到,可以理解成是一个链 上挂若干仙人掌,存在边 与 ,形成一个环,注意到这里 在子图中只包含了两条边,是符合 要求的。
于是我们需要维护此类链的信息,定义为 。
在转移之前,规定单独一个点不属于 或 定义范畴内,但是属于 的。
转移 :初始状态下链是由两个仙人掌拼起来的。
其中
转移 :在链 的一端增加一个点 。
其中 。
为了去重,钦定 。
转移 ,将一个链用两条边并成一个符合 要求的环。
其中 。
转移 ,子仙人掌第一种情况,将环套在跟上。
其中 。
转移 ,子仙人掌第二情况,由一条链引出来。
其中 。
要注意的是:在后两个转移中,加入一个子仙人掌会导致算重,因此我们钦定加入的子仙人掌为状态 的 对应节点所在子仙人掌。
同时转移 的情况会在转移 中再传递给 。(毕竟 是 的特殊情况。)
点击查看代码
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
int u=read(),v=read();
E[u][v]=E[v][u]=1;
}
for(int i=1;i<=n;++i) f[(1<<i-1)][i]=1;
for(int s=1;s<(1<<n);++s){
for(int u=1;u<=n;++u){
if(!(s&(1<<u-1))) continue;
for(int v=1;v<=n;++v){
if(u==v) continue;
if(!(s&(1<<v-1))) continue;
if(!E[u][v]) continue;
for(int t=s^(1<<v-1);t;t=(t-1)&(s^(1<<v-1))){
if(!(t&(1<<u-1))) continue;
int t1=t,t2=s^t1;
h[s][u][v]=(h[s][u][v]+1ll*f[t1][u]*f[t2][v]%mod)%mod;
}
}
for(int v=1;v<=n;++v){
if(u==v) continue;
if(!(s&(1<<v-1))) continue;
if(!E[u][v]) continue;
for(int w=1;w<=n;++w){
if(u==w||v==w) continue;
if(!(s&(1<<w-1))) continue;
for(int t=s^(1<<v-1)^(1<<w-1);t;t=(t-1)&(s^(1<<v-1)^(1<<w-1))){
if(!(t&(1<<u-1))) continue;
int t1=t,t2=s^t1;
h[s][u][w]=(h[s][u][w]+1ll*f[t1][u]*h[t2][v][w]%mod)%mod;
}
}
}
for(int v=1;v<=n;++v){
if(u==v) continue;
if(!(s&(1<<v-1))) continue;
if(!E[u][v]) continue;
for(int w=v+1;w<=n;++w){
if(!(s&(1<<w-1))) continue;
if(!E[u][w]) continue;
g[s][u]=(g[s][u]+h[s^(1<<u-1)][v][w])%mod;
}
}
if(s!=(1<<u-1)){
int v=__lg(lowbit((s^(1<<u-1))))+1;
for(int t=s^(1<<v-1);t;t=(t-1)&(s^(1<<v-1))){
if(!(t&(1<<u-1))) continue;
int t1=t,t2=s^t1;
f[s][u]=(f[s][u]+1ll*f[t1][u]*g[t2|(1<<u-1)][u]%mod);
for(int w=1;w<=n;++w){
if(!(t2&(1<<w-1))) continue;
if(!E[u][w]) continue;
f[s][u]=(f[s][u]+1ll*f[t1][u]*f[t2][w]%mod)%mod;
}
}
}
}
}
printf("%d\n",f[(1<<n)-1][1]);
return 0;
}
作者:SoyTony
出处:https://www.cnblogs.com/SoyTony/p/Solution_on_QOJ-7412.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效