【NOIP2013模拟9.29】Mixing Chemicals
题目描述
路人甲的实验室有 n 瓶化学药品,编号为 0 到 n-1,你知道第 i 瓶只有和第 c[i]瓶放在一起才会发生爆炸。为了整理实验室,你需要将他们装进 k 个不同的盒子里。显然,为了你的生命安全,你不能把两瓶会造成爆炸的药品放进同一个箱子。你希望计算出有多少中不同的方案。为了降低难度,你只需要将答案 mod 1000000007。
输入格式
第一行一个整数 T,表示有 T组测试数据。
对于每组数据
第一行两个整数 n,k
第二行 n 个整数表示 c[i]
输出格式
基环树DP
对于每组数据输出一行一个整数。
题解在代码的头注释中:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }