[dp] Jzoj P3460 Mixing Chemicals
题解
- 题目大意:给n瓶化学药品和k个盒子,每瓶化学药品和一瓶化学药品不能放在一个盒子里,求放化学药品的方案数
- 我们可以将不能放在同一个盒子的两瓶化学药品连一条边,那么就形成了一个图,而且每个点的出都只有1
- 那么考虑一下染色,一条边的两个点不能染成同一种颜色
- 其实这样的话,就可以设f[i][0/1]为第i个点,染0色或1色的方案数
- f[i][0]=(f[i-1][1]*(k-1)+f[i-1][0]*(k-2))%mo
- f[i][1]=f[i][0]
- 这个可以预处理出来
- 然后我们将每个点在哪个环求出来,在求出连通块的个数,然后ans=ans* (f[lt,1]*k%mo)%mo lt为当前循环的搜到的连通块的个数
- 求出将每个连通块的填颜色的个数相乘就求出ans
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 using namespace std; 5 const int inf=1000000000,mo=1000000007; 6 long long ans,a[110],d[110],tot,num,f[110][3]; 7 int n,k,t; 8 int main() 9 { 10 for (scanf("%d",&t);t;t--) 11 { 12 memset(a,0,sizeof(a)); memset(d,0,sizeof(d)); memset(f,0,sizeof(f)); 13 scanf("%d%d",&n,&k); 14 for (int i=0;i<=n-1;i++) 15 { 16 scanf("%lld",&a[i]); 17 d[i]=inf; 18 } 19 f[0][1]=1; f[1][0]=k-1; 20 for (int i=2;i<=n;i++) 21 { 22 f[i][0]=(f[i-1][1]*(k-1)+f[i-1][0]*(k-2))%mo; 23 f[i][1]=f[i-1][0]; 24 } 25 num=0; ans=1; 26 for (int i=0;i<=n-1;i++) 27 if (d[i]==inf) 28 { 29 int x=i; 30 while (d[x]==inf) d[x]=i,x=a[x]; 31 if (d[x]!=i) continue; 32 int y=x; tot=1; x=a[x]; 33 while (x!=y) tot++,x=a[x]; 34 ans=ans*(f[tot][1]*k%mo)%mo; 35 n-=tot; 36 } 37 for (int i=1;i<=n;i++) (ans*=(k-1))%=mo; 38 printf("%lld\n",ans); 39 } 40 }