zoukankan      html  css  js  c++  java
  • BZOJ 4455: [Zjoi2016]小星星

    我称之为补题解,感觉这可以帮助我把之前做过的没写题解的一些题目(好吧坑了好多题目)都补起来

    首先容易想到(O(n! imes n))的大暴力,然后套路地发现在树上可以化为子集问题

    我们设(f_{i,j,k})表示(i)的子树内,(i)映射为(j)之后且所有点映射完后构成了图上的点集(k)(状压)的方案数,显然我们可以在枚举一个子节点的同时枚举父节点的所有状态,然后再对于它的状态补集枚举子集即可,复杂度应该是(O(3^n imes n))

    然而这样无法通过此题,我们细细分析以下会发现现在的复杂度瓶颈主要是在枚举子集上,考虑如果我们能去掉后面那一维该有多好

    考虑我们假设映射可以重复,那么显然此时会多算出一部分,比如映射过去的集合比全集少了一些元素

    很直观地,我们考虑再减去少了一个元素的方案数,然后此时少了两个元素的又被多减了需要再加回去……

    想到了什么,容斥啊!我们可以对映射过去的集合进行容斥,每次只把点映射到这个集合里去,然后算出此时的方案数即可

    那么此时的方程显然就是:

    [f_{x,j}=prod_{yin son(x)} sum_{j=1}^n f_{y,j} imes g_{i,j} ]

    最后(sum_{i=1}^n f_{1,i})就是方案数,根据集合的元素取正负号即可

    复杂度(O(2^n imes n^3)),可能需要卡常(代码是一年前的了233)

    #include<cstdio>
    #include<vector>
    #define RI register int
    using namespace std;
    const int N=20;
    int n,m,x,y,status,id[N],cnt; bool p[N][N];
    vector <int> v[N]; long long ans,f[N][N];
    inline void DFS(int now,int fa)
    {
    	RI i,j,k; int lim=v[now].size(),to;
    	for (i=0;i<lim;++i) if ((to=v[now][i])!=fa) DFS(to,now);
    	for (i=1;i<=cnt;++i)
    	{
    		for (f[now][id[i]]=1,j=0;j<lim;++j)
    		if ((to=v[now][j])!=fa)
    		{
    			long long ret=0; for (k=1;k<=cnt;++k)
    			if (p[id[i]][id[k]]) ret+=f[to][id[k]];
    			f[now][id[i]]*=ret;
    		}
    	}
    }
    int main()
    {
    	RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d%d",&x,&y),p[x][y]=p[y][x]=1;
    	for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
    	for (status=(1<<n)-1,i=0;i<=status;++i)
    	{
    		for (cnt=j=0;j<n;++j) if ((i>>j)&1) id[++cnt]=j+1;
    		DFS(1,0); for (j=1;j<=cnt;++j) ans+=((n-cnt)&1?-1LL:1LL)*f[1][id[j]];
    	}
    	return printf("%lld",ans),0;
    }
    
  • 相关阅读:
    配置并学习微信JS-SDK(1)
    在 Sublime Text 3 中运行 PHP
    apache-配置虚拟主机
    mysql-积累管理sql语句
    博客园-推荐发博客工具的安装配置
    注释多行文本
    nargin
    Sublime Text
    The MATLAB Profiler
    Fig723.asy
  • 原文地址:https://www.cnblogs.com/cjjsb/p/12240392.html
Copyright © 2011-2022 走看看