【NOIP2013模拟9.29】Mixing Chemicals

题目描述

路人甲的实验室有 n 瓶化学药品,编号为 0 到 n-1,你知道第 i 瓶只有和第 c[i]瓶放在一起才会发生爆炸。为了整理实验室,你需要将他们装进 k 个不同的盒子里。显然,为了你的生命安全,你不能把两瓶会造成爆炸的药品放进同一个箱子。你希望计算出有多少中不同的方案。为了降低难度,你只需要将答案 mod 1000000007。

输入格式

第一行一个整数 T,表示有 T组测试数据。

对于每组数据

第一行两个整数 n,k

第二行 n 个整数表示 c[i]

输出格式

对于每组数据输出一行一个整数。

 

题解在代码的头注释中:

 

 1 /*mix:把不能放一起的药连一条边。
 2 最后会得到若干环套树
 3 (一个环,每个节点可以是一棵树),
 4 也可能退化成一棵树,
 5 我们可以理解成环上只有一个节点的环套树。
 6 现在要将其k染色,使得有边相连的节点颜色不同。
 7 我们可以DP求得一个环的方案数,
 8 那么对于环上连出的一棵树,
 9 它的方案数显然是(k-1)^Size
10 (这里的Size是不算在环上的那个节点的这课树的节点数)。
11 最后只要把环的方案数*所有树的方案数的乘积就是最终答案。*/
12 #include<iostream>
13 #include<cstdio>
14 #include<cmath>
15 #include<algorithm>
16 #include<cstring>
17 #define ms(a,x) memset(a,x,sizeof(a))
18 using namespace std;
19 const int MAXN=105;
20 const int mod=1e9+7;
21 long long n,m,k,t,tot,ans,p,num;bool vis[MAXN];
22 long long c[MAXN],f[MAXN],fa[MAXN];bool bz[MAXN];
23 int main(){
24     scanf("%lld",&t);
25     while(t--){
26         scanf("%lld%lld",&n,&k);num=n;ms(vis,0);
27         for(int i=0;i<n;i++) scanf("%lld",&fa[i]);
28         f[1]=k;f[2]=k*(k-1)%mod;f[3]=f[2]*(k-2)%mod;ans=1;
29         for(int i=4;i<=n;i++) f[i]=((f[i-1]*(k-2)%mod)+(f[i-2]*(k-1)%mod))%mod;
30         for(int i=0;i<n;i++)
31         if(!vis[i]){
32             for(p=i,tot=0,ms(bz,0);!bz[p];bz[p]=1,p=fa[p],tot++);
33             if(p^i) continue;
34             for(p=i,ms(bz,0);!bz[p];bz[p]=vis[p]=1,p=fa[p]);
35             num-=tot;ans=(1LL*ans*f[tot])%mod;
36         }
37         for(int i=1;i<=num;i++){
38             ans=ans*(k-1)%mod;
39         } printf("%lld\n",ans);
40     } return 0;
41 }
基环树DP

 

 

posted @ 2018-08-08 21:03  杜宇一声  阅读(133)  评论(0编辑  收藏  举报