【题解】QOJ-7412 Counting Cactus

Page Views Count

之前没做过仙人掌,甚至没学过圆方树,理解了半天改出来了,遂记。

尝试用理解树的方法理解一个仙人掌,钦定根为 u,那么一个与 u 有关的子仙人掌(类比子树)有如下两种情况:

  • 一个 u 所在的环,环中除了 u 以外的节点挂任意仙人掌,套在根上。

  • 一个 u 引出的边 (u,v),以 v 为根的一个仙人掌。

结合上图,110 号节点构成的子仙人掌就是以环的形式套在根上,而另一个子仙人掌则是由 11 引出的,属于第二种情况。

最基本的状态设 f(S,u) 为点集 S 中以 u 为根的仙人掌个数。

根据上面对子仙人掌的讨论,我们知道第一种情况要求 u 的连边恰好有一个环,需要另设状态 g(S,u) 表示(具体定义同 f);而第二种情况只要枚举连边再增加一个 f(T,v) 即可。

思考符合 g 定义的仙人掌怎么得到,可以理解成是一个链 (v,w) 上挂若干仙人掌,存在边 (u,v)(u,w),形成一个环,注意到这里 u 在子图中只包含了两条边,是符合 g 要求的。

于是我们需要维护此类链的信息,定义为 h(S,u,v)

在转移之前,规定单独一个点不属于 gh 定义范畴内,但是属于 f 的。


转移 1:初始状态下链是由两个仙人掌拼起来的。

f(T,u)×f(ST,v)h(S,u,v)

其中 TS,uT,vST,(u,v)E


转移 2:在链 (v,w) 的一端增加一个点 u

f(T,u)×h(ST,v,w)h(S,u,w)

其中 TS,uT,v,wST,(u,v)E

为了去重,钦定 v<w


转移 3,将一个链用两条边并成一个符合 g 要求的环。

h(S{u},v,w)g(S,u)

其中 u,v,wS,(u,v),(u,w)E


转移 4,子仙人掌第一种情况,将环套在跟上。

f(T,u)×g(ST{u},u)f(S,u)

其中 TS,uT


转移 5,子仙人掌第二情况,由一条链引出来。

f(T,u)×f(ST,v)f(S,u)

其中 uT,vST,(u,v)E


要注意的是:在后两个转移中,加入一个子仙人掌会导致算重,因此我们钦定加入的子仙人掌为状态 S{u} lowbit 对应节点所在子仙人掌

同时转移 3 的情况会在转移 4 中再传递给 f。(毕竟 gf 的特殊情况。)

点击查看代码
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 国际」许可协议进行许可。

posted @   SoyTony  阅读(97)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示