那永恒没骗我 离散也没有 这份爱只是活在 另外时空

那永恒没骗我 离散也没有
这份爱只是活在 另外时空
如果 每一声再见 会开启
一个新的空间
每做一个梦 是时空 偶尔的重叠
你好吗 离开的人啊
请帮我 相爱到剧终
\(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\) ——周深 《花开忘忧》

题意

\(~~~~\) 圆上有 \(2n\) 个点,保证任意选出六个点连边不会交于一点,求有多少连边方案使得连完过后恰有 \(n-1\) 个交点且这些交点形成一棵树.
\(~~~~\) \(1\leq n\leq 20\).

题解

\(~~~~\) 我宣布ubuntu的输入法就算是fcitx也很难用,想办法安个搜狗。

\(~~~~\) AT 的题不出意外会有一个思路然后这个思路出了意外。

\(~~~~\) 这题一开始想成共有 \(n\) 个点,自然而然考虑到状压。然后发现可以不关心已经选了的点怎么连的边,因为只要新连边的两个端点跨过了其中一个端点即可。于是有了美好的 \(\mathcal{O(2^n n^3)}\) 的算法,然后发现是 \(2n\) ,然后自闭。

\(~~~~\) 然后顺着刚刚的思路,我们只需要关心一个区间的端点中间只包含了一个连出去的边,所以我们定义 \(dp[l,r,x]\) 表示 \([l,r]\) 区间内 \(x\) 向区间外连边,其他内部连的方案数。

\(~~~~\) 然后考虑怎么转移这样一个东西过来,枚举状态 \([l,r,i]\)\(n^3\) ,然后我们枚举两个端点 \(j,k\) 表示这两个端点连边,那么这个端点就会与已经枚举的 \(x\) 有交,这也是唯一的交。然后我们来看更其他的点。可以发现要么这些边的端点在 \(j\) 两侧,要么在 \(i\) 两侧,要么在 \(k\) 两侧。更进一步地,存在两个分割点 \(p,q\) ,使得 \([l,p]\) 内的互相连边,\([p+1,q-1]\) 内的互相连边,\([q,r]\) 内的互相连边,并且甚至我们已经确定了连出去的点,所以我们就得到了三个子问题。

\(~~~~\) 所以 \(dp_{l,i,r}=dp_{p+1,i,q-1}\times dp_{q,k,r} \times dp_{l,j,p}\times [a_{j,k}=1]\) ,这就有了 \(n^7\) 的转移。

\(~~~~\) 不过这东西常数挺小然后可以过。

\(~~~~\)\(g_{l,k,p}=\sum f_{l,j,p}\times [a_{j,k}=1]\) ,这可以在每个状态过后用 \(n^2\) 完成。

\(~~~~\) 最后只需要每次枚举完状态过后 \(n^2\) 就可以了.

\(~~~~\) 最后再吐槽一次输入法

代码

#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x*=f;
}
char Map[45][45];
ll f[45][45][45],g[45][45][45];
int main() {
    #ifndef ONLINE_JUDGE
    freopen("input","r",stdin);
    freopen("output","w",stdout);
    #endif
    int n;read(n);n*=2;
    for(int i=1;i<=n;i++) scanf("%s",Map[i]+1);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) Map[i][j]-='0';
    for(int i=2;i<=n;i++)
    {
        f[i][i][i]=1;
        for(int j=i+1;j<=n;j++) g[i][j][i]=Map[i][j];
    }
    for(int len=3;len<n;len+=2)
    {
        for(int l=2;l+len-1<=n;l++)
        {
            int r=l+len-1;
            for(int p=l;p<=r;p+=2)
            {
                for(int q=r;q>=l;q-=2)
                {
                    ll Tmp=0;
                    for(int k=q;k<=r;k++) Tmp+=g[l][k][p]*f[q][k][r];
                    for(int i=p+1;i<q;i++) f[l][i][r]+=f[p+1][i][q-1]*Tmp;
                }
            }
            for(int i=l;i<=r;i++)
                for(int j=i+1;j<=n;j++) g[l][j][r]+=Map[i][j]*f[l][i][r];
        }
    }
    ll Ans=0;
    for(int i=2;i<=n;i++) Ans+=Map[1][i]*f[2][i][n];
    printf("%lld",Ans);
    return 0;
}
posted @ 2022-09-23 22:44  Azazеl  阅读(84)  评论(0编辑  收藏  举报