P9174 [COCI 2022/2023 #4] Bojanje
题目要求我们求出至少有 \(k\) 个不同颜色的概率。
处理概率问题很容易想到 dp 处理,并且问题中的 \(n\) 很小,但是转移次数 \(t\) 很大,明显是一个矩阵乘法优化 dp 的问题。
那么方法有了就可以考虑转移式了。
如果按照常规的思路,以 \(dp_i\) 表示包含 \(i\) 种不同颜色的概率明显是不行的,因为有 \(i\) 种不同颜色其中包含了多种情况,并且其中的每一种情况转移到另一种情况概率不一定是相同的,而且这些不同的情况分布并不均匀,所以无法直接得到答案。
那么既然在一个状态里面含有多种情况,那么我们完全可以把这个状态拿出来。
我们给每种情况记录一个编号,开始时先处理出每一种可以转移到的情况,然后继续搜索,将转移方式记录到矩阵中。
最后直接矩阵快速幂即可。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,t,k,ys,id,a[15],b[15];
map<int,int> mp;
bool vis[55];
vector<int> ans;
struct MT{
int n,m,c[55][55];
MT(){
memset(c,0,sizeof(c));
n=m=0;
}
MT friend operator*(MT a,MT b){
MT c;
c.n=a.n,c.m=b.m;
for(int i=1;i<=a.n;i++){
for(int j=1;j<=b.m;j++){
for(int k=1;k<=a.m;k++){
c.c[i][j]+=a.c[i][k]*b.c[k][j]%mod;
c.c[i][j]%=mod;
}
}
}
return c;
}
}be,base;
void jzksm(int b){
while(b){
if(b&1)be=be*base;
base=base*base;
b>>=1;
}
}
int ksm(int a,int b){
int x=a,s=1;
while(b){
if(b&1)s=(s*x)%mod;
x=(x*x)%mod;
b>>=1;
}
return s;
}
void bfs(){
queue<int> q;
int s=0;
for(int i=1;i<=n;i++)s=s*11+1;
q.push(s);
mp[s]=1;
id=1;
while(!q.empty()){
int u=q.front();
q.pop();
if(vis[mp[u]])continue;
vis[mp[u]]=1;
int tmp=u,sum=0;
for(int i=1;i<=n;i++)a[i]=tmp%11,tmp/=11;
for(int i=1;i<=n;i++)if(a[i])sum++;
if(sum>=k)ans.push_back(mp[u]);
for(int i=1;i<=n;i++){
if(!a[i])continue;
for(int j=1;j<=n;j++){
if(!a[j])continue;
for(int k=1;k<=n;k++)b[k]=a[k];
b[i]--,b[j]++;
sort(b+1,b+n+1);
int s=0;
for(int k=n;k>=1;k--)s=s*11+b[k];
if(mp[s])base.c[mp[u]][mp[s]]+=a[i]*a[j]%mod;
else {
mp[s]=++id;
q.push(s);
base.c[mp[u]][id]+=a[i]*a[j]%mod;
}
}
}
}
}
signed main(){
cin>>n>>t>>k;
ys=ksm(n*n,mod-2);
bfs();
be.n=1,be.m=id;
be.c[1][1]=1;
base.n=base.m=id;
for(int i=1;i<=id;i++){
for(int j=1;j<=id;j++){
base.c[i][j]=base.c[i][j]*ys%mod;
}
}
jzksm(t);
int res=0;
for(int i:ans){
res+=be.c[1][i];
res%=mod;
}
cout<<res;
return 0;
}

浙公网安备 33010602011771号