【JZOJ B组】【NOIP2013模拟9.29】Mixing Chemicals

解决一个化学药品分箱问题,确保不会发生爆炸。通过构建图结构并使用DFS和DP算法来计算不同方案的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

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

Input

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

对于每组数据

第一行两个整数n,k

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

Output

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

Sample Input

3

3 3

1 2 0

4 3

1 2 0 0

3 2

1 2 0

Sample Output

6

12

0

Data Constraint

1<=T<=50 1<=n<=100 2<=k<=1000 0< Ci < n ,i≠c[i]

对于30%的数据T,n,k<=50

思路

我们可以见一个图,把会爆炸的点连在一起。
于是问题就变成一个染色问题。

首先,我们观察图的性质,可以发现,这个图由若干个环套树组成。
所以我们只要求出环和链即可(dfs)

我们可以DP预处理处环和链

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1077,mod=1e9+7;
long long f1[maxn],f2[maxn],ans=1;
int t,n,k,d[maxn],cnt,res;
vector<int> v[maxn];
void init()
{
    memset(d,0,sizeof(d));
    for(int i=0; i<n; i++) v[i].clear();
    f1[1]=k; f1[2]=k*(k-1); f1[3]=k*(k-1)*(k-2);
    for(int i=4; i<=n; i++) f1[i]=((k-2)*f1[i-1]%mod+(k-1)*f1[i-2]%mod)%mod;
    f2[0]=1;
    for(int i=1; i<=n; i++) f2[i]=(k-1)*f2[i-1]%mod;
}
void dfs(int u,int fa,int dep)
{
    d[u]=dep; cnt++;
    for(int i=0; i<v[u].size(); i++) if(v[u][i]!=fa)
    {
        if(d[v[u][i]]&&d[v[u][i]]<dep) res=dep-d[v[u][i]]+1;
        if(!d[v[u][i]]) dfs(v[u][i],u,dep+1);
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        if(k==1)
        {
            printf("%d\n",n-1?0:1);
            continue;
        }
        init();
        for(int i=0;i<n;i++)
        {
            int c;
            scanf("%d",&c);
            v[i].push_back(c); v[c].push_back(i);
        }
        ans=1;
        for(int i=0;i<n;i++) if(!d[i])
        {
            cnt=res=0;
            dfs(i,-1,1);
            if(!res)res=2;
            ans=ans*f1[res]%mod*f2[cnt-res]%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值