ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

5739. 毒奶

2022-07-09 08:32:16  阅读:158  来源: 互联网

标签:prime 毒奶 现实 城市 世界 枚举 5739 对应


Description

现实世界中有 \(n\) 个城市,编号为 \(1 \sim n\),城市之间有道路相连,道路是双向的,小 \(\mathrm{C}\) 可以从道路的任意一端走到另一端。因为交通并不发达,所以一些城市之间可能无法互相到达。

梦境和现实是非常相似的,所以幻想世界中也有 \(n\) 个城市,编号为 \(1^{\prime} \sim n^{\prime}\),还有一些双向道路。

小 \(\mathrm{C}\) 相信梦境和现实并没有本质的区别。她选择了一种方法,把梦境中的城市和现实中的城市一一对应起来,从而在两个世界对应的城市之间移动。具体地, 如果现实世界的城市 \(a\) 对应幻想世界的城市 \(b^{\prime}\) 那么小 \(\mathrm{C}\) 可以从 \(a\) 走到 \(b^{\prime}\) 或从 \(b^{\prime}\) 走到 \(a\)。因为幻想世界的交通也并不发达,所以两个世界中的道路共有恰好 \(n-1\) 条。

这样一来,小 C 周游世界的可行性就取决于对应方法的选取。定义一种对应方法是合法的,当且仅当这种对应方法使小 \(C\) 能够周游世界。

小 \(C\) 想知道,有多少种对应方法是合法的。两种对应方法不同,当且仅当存在一个现实世界的城市,在两种方法中对应不同的幻想世界的城市。

因为答案可能非常大,所以你只需要告诉她答案模 998244353 的值。

Solution

注意到 \(n\le 20\),这启示我们使用状压 DP。

初始情况下有 \(2n\) 个点,\(n-1\) 条边,所以会形成 \(n+1\) 个联通块。

用并查集记录每个联通块的大小。枚举每个状态,求出各状态下所包含的点的数量。

有一个比较显然的结论是,只有在左右两边联通块的总点数相同的情况下,才能在这两个联通块之间连边。求出总方案 \(g_s\)。

然后枚举每个状态(注意不要枚举无用状态),因为合法方案 = 总方案 - 非法方案,所以考虑如何得出非法方案。至于非法的情况就是在 \(s\) 这个状态下,\(t\) 是 \(s\) 的子集,而 \(t\) 所含有的连通块和 \(s^t\) 所含有的连通块各自连边,方案数是 \(g_t*f_{s \text{^} t}\)。所以只需要从 \(s\) 中将编号最小的连通块取出,得到状态 \(ss\) ,随后枚举 \(ss\) 的子集,转移即可。

Code

#include<cstdio>
#define N 21
#define mod 998244353
#define ll long long
using namespace std;
int n,m,x,y,tot,n1,n2,s1,s2,all,fi[N],size[N],sz1[1<<N],sz2[1<<N];
ll fac[N],g[1<<N],f[1<<N];
int find(int x)
{
    if (fi[x]!=x) fi[x]=find(fi[x]);
    return fi[x];
}
int main()
{
    freopen("milk.in","r",stdin);
    freopen("milk.out","w",stdout);
    scanf("%d%d",&n,&m);
    fac[0]=1;
    for (int i=1;i<=n;++i)
        fac[i]=fac[i-1]*(ll)i%mod;
    for (int i=1;i<=n;++i)
        fi[i]=i,size[i]=1;
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        x=find(x);y=find(y);
        if (x!=y) fi[y]=x,size[x]+=size[y];
    }
    for (int i=1;i<=n;++i)
        if (find(i)==i)
        {
            sz1[1<<tot]=size[i];
            ++tot;
        }
    n1=tot;tot=0;s1=(1<<n1)-1;
    for (int i=1;i<=n1;++i)
        for (int j=1;j<=s1;++j)
            if ((j>>(i-1))&1) sz1[j]+=sz1[j^(1<<(i-1))];
    for (int i=1;i<=n;++i)
        fi[i]=i,size[i]=1;
    for (int i=1;i<=n-1-m;++i)
    {
        scanf("%d%d",&x,&y);
        x=find(x);y=find(y);
        if (x!=y) fi[y]=x,size[x]+=size[y];
    }
    for (int i=1;i<=n;++i)
        if (find(i)==i)
        {
            sz2[1<<tot]=size[i];
            ++tot;
        }
    n2=tot;tot=0;s2=(1<<n2)-1;
    for (int i=1;i<=n2;++i)
        for (int j=1;j<=s2;++j)
            if((j>>(i-1))&1) sz2[j]+=sz2[j^(1<<(i-1))];
    all=(1<<(n1+n2))-1;
    if (n1+n2!=n+1) {printf("0\n");return 0;}
    for (int s=1;s<=all;++s)
    {
        int num1=sz1[s&s1],num2=sz2[s>>n1];
        if (num1==num2) g[s]=fac[num1];
    }
    for (int s=1;s<=all;++s)
    {
        if (!g[s]) continue;
        f[s]=g[s];
        int ss=s^(s&(-s));
        for (int t=ss;t;t=(t-1)&ss)
            f[s]=(f[s]-(ll)(g[t]*f[s^t])%mod+mod)%mod;
    }
    printf("%lld\n",f[all]);
    return 0;
}

标签:prime,毒奶,现实,城市,世界,枚举,5739,对应
来源: https://www.cnblogs.com/Livingston/p/16460099.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有