zoukankan      html  css  js  c++  java
  • SP8064 AMR10J-Mixing Chemicals

    SP8064 AMR10J - Mixing Chemicals

    给一个(n)个节点的基环树森林,求(k)分图染色的方案数

    先考虑一棵树的方案,随机选一个点为根,然后其它每个节点都有唯一一个前驱,根往叶子节点确定颜色,根有(k)种染色方案,其他节点只要与前驱不同即可,染,色方案有(k-1)种,乘法原理,假设这棵树的节点数为(n),这棵树(k)分图染色的方案数是(k(k-1)^{n-1})

    对于基环树来说,先确定环的颜色,再确定其它点的颜色,同样的,其他点只有唯一一个前驱,所以方案数还是(k-1),不妨设给一个大小为(x)的环进行(k)分图染色方案为(f_x),那么给定节点(n),环大小为(x)的基环树(k)分图染色的方案数就是(f_x(k-1)^{n-x})
    森林的方案可以乘起来,接下来就是如何求(f_x)

    算法一:dp

    暴力(dp)

    (g_{i,j,k})表示考虑完前(i)个节点放的颜色,第(i)个节点颜色为(j),第(1)个节点颜色为(k)的方案数,(f_x=sumlimits_{i ot=j}g_{x,i,j})

    考虑减小状态数量,每种颜色 本质没有区别,我们不用关系颜色具体是什么,仅关系选出的颜色与第一个节点颜色是否相同,不妨设(g_{i,0/1})考虑完前(i)个节点放的颜色,第(i)个节点的颜色是否与第一个节点颜色相同方案数,

    (0)代表相同,(1)代表不同,(f_x=g_{x,0}) 朴素转移:

    [large g_{x,0}=g_{x-1,1},g_{x,1}=g_{x-1,0} imes(k-1)+g_{x-1,1} imes(k-2) ]

    第一个转移:(x)(1)相同,该点只有一种选择,同时(x-1)肯定和(1)不相同;

    第二个转移:(x)(1)不同,(x-1)(1)相同时,(x)(k-1)种选择,(x-1)(1)不同时,该点只有(k-2)种选择

    由这个柿子,我们可以看出,不妨用(g_{x,0})代表(g_{x-1,1}),用(f_x)代表(g_{x,0}),转移也可以写成

    (large f_{x+1}=f_{x-1} imes(k-1)+f_x imes(k-2))

    [large f_x=f_{x-1} imes(k-2)+f_{x-2} imes(k-1) ]

    一个标准的矩阵快速幂柿子,直接(O(n))求和,复杂度(O(sum n))

    算法二:数学

    对于一个大小为(n)的环最终染色结果,如果允许相邻点颜色相同,我们就认为它们间的边被标记了

    显然标记(x(x ot=n))条边方案数为(large{nchoose x}f_{n-x}),把同色点缩点即可证明,

    标记边数量等于等于(n),方案数即为(k),(所有边颜色相同)

    标记边数量可能为(0,1,..,n-2,n)(不能有(n-1),因为不可能只标记(n-1)条边),画图可知,

    任意染色方案数为(k^n),所以可以得到以下柿子

    [k^n=sum_{i=0}^{n-2}{nchoose i}f_{n-i}+k ]

    [k^n=sum_{i=2}^n{nchoose i}f_i+k ]

    手膜一下,把范围扔进柿子就好了,现在就可以(O(n^2))(f)

    二项式反演降低复杂度:

    [largesum_{i=0}^n{nchoose i}f_i=egin{cases}1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n=0\ k^n-k+kn+1~~~~n>0 end{cases} ]

    (large g_n=sumlimits_{i=0}^n{nchoose i}f_i),那么

    [large f_n=sum_{i=0}^n(-1)^{n-i}{nchoose i}g_i ]

    const int mod = 1e9 + 7,N = 105;
    inline void write(int x) {
      static int sta[64];
      int top = 0;
      do {
        sta[top++] = x % 10, x /= 10;
      } while (x);
      while (top) putchar(sta[--top] + 48); putchar('
    ');
    }    
    int mo(const int x){return x >= mod ? x-mod : x;}
    int g[N],tot,ok[N],st[N],vis[N],c[N];
    int dfs(int u){
    	if(vis[u]) return ok[u] ? -1 : vis[u];
    	st[++tot] = u; vis[u] = tot;
    	return dfs(c[u]);
    }
    int main(){
    	int T = read();
    	while(T--){
    		int n,k; g[0] = 1; g[1] = 0;
    		n = read(); k = read();
    		for(int i = 2;i <= n;++i)
    			g[i] = mo(1ll*g[i-1]*(k-2)%mod + 1ll*g[i-2]*(k-1)%mod);
    		for(int i = 0;i <= n;++i)
    			g[i] = 1ll*g[i]*k % mod;
    		for(int i = 1;i <= n;++i)
    			c[i] = read(),vis[i] = ok[i] = 0,++c[i];
    		int sum = n,ans = 1;
    		for(int i = 1;i <= n;++i){
    			if(!vis[i]){
    				tot = 0;int tmp = dfs(i);
    				for(int j = 1;j <= tot;++j)
    					ok[st[j]] = true;
    				if(~tmp){
    					tmp = tot - tmp + 1;sum -= tmp;
    					ans = 1ll*ans*g[tmp] % mod;
    				}
    			}
    		}
    		int sna = 1;
    		while(sum--)
    			sna = 1ll*sna*(k-1) % mod;
    		write(1ll*ans*sna%mod);
    	}
    }
    
  • 相关阅读:
    windows代码,传入文件名,遍历此目录下所有文件.
    windows,分割路径.得出目录
    windows代码,路径分割
    windows下,读取快捷方式lnk所指向的路径
    【Unity】3.2 利用预设(Prefab)制作可复用的组件
    【Unity】3.1 利用内置的3D对象创建三维模型
    【Unity】3.0 第3章 创建和导入3D模型
    【Unity】2.11 了解游戏有哪些分类对你开阔思路有好处
    【Unity】2.10 利用VS2015编辑Unity的C#脚本
    【Unity】2.9 光源(Lights)
  • 原文地址:https://www.cnblogs.com/shikeyu/p/13870786.html
Copyright © 2011-2022 走看看