2022牛客暑期多校训练营1 I Chiitoitsu
纯属个人训练记录,方便以后复题用 以下均代表个人对题解的理解
题意:(借用自牛客题解) 题解:实因为每种牌数量都是4张,并且初始牌中不会出现超过三张一样的牌,所以我们可以把牌分成两类,一类是已经成队了的,另一类是单牌,显然已经成队的不会再进行替换,可以当作没有,而单牌因为我知道已经打出了哪些牌,所有我每次总可以使得手牌中的单牌都是之前还没有出现过的单牌,即牌堆中还有
3
3
3张一样的,这样我们可以预处理一下,用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示手上还有
i
i
i张单排,牌堆还有
j
j
j张牌时还需要摸牌次数的期望值,那么我根据当前摸的牌是否可以和我手上的单牌配对可分为两种: 1.可以和当前手上单牌配对,那么配对后需要再扔掉一张单牌,当前单牌数减少二(一张被配对了,一张被扔掉了),牌堆内牌数减一,概率为
3
?
i
j
\frac{3*i}{j}
j3?i?。 2.不可以和当前手上单牌配对,那么我必定扔掉(因为手上原本的单牌一定有三张还没出现的在牌堆,他们遇到能匹配的概率一定是等于或优于当前摸到的单牌),那么当前单牌数不变,牌堆内牌数减一,概率为
j
?
3
?
i
j
\frac{j-3*i}{j}
jj?3?i?。
状态转移方程为:
d
p
[
i
]
[
j
]
=
1
+
3
?
i
j
?
d
p
[
i
?
2
]
[
j
?
1
]
+
j
?
3
?
i
j
?
d
p
[
i
]
[
j
?
1
]
dp[i][j]=1+\frac{3*i}{j}*dp[i-2][j-1]+\frac{j-3*i}{j}*dp[i][j-1]
dp[i][j]=1+j3?i??dp[i?2][j?1]+jj?3?i??dp[i][j?1], 注意当
i
i
i为1时:
d
p
[
1
]
[
j
]
=
1
+
3
j
?
0
+
j
?
3
j
?
d
p
[
i
]
[
j
?
1
]
=
1
+
j
?
3
j
?
d
p
[
i
]
[
j
?
1
]
dp[1][j]=1+\frac{3}{j}*0+\frac{j-3}{j}*dp[i][j-1]=1+\frac{j-3}{j}*dp[i][j-1]
dp[1][j]=1+j3??0+jj?3??dp[i][j?1]=1+jj?3??dp[i][j?1] 预处理出
d
p
dp
dp数组后,每次输入判断下有多少张单牌即可 细节和不懂可以看代码
ll mi(ll a,ll b){
ll res=1;
while(b){
if(b%2){
res=res*a%mod;
}
a=a*a%mod;
b/=2;
}
return res;
}
map<string,bool>mp;
ll dp[20][200];
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
ll sum=34*4-13;
for(ll i=3;i<=sum;i++){
dp[1][i]=(1+(((i-3)*mi(i,mod-2)%mod)*dp[1][i-1]%mod))%mod;
}
for(ll i=3;i<=13;i+=2){
for(ll j=3;j<=sum;j++){
dp[i][j]=(1+(((i*3)*mi(j,mod-2)%mod)*dp[i-2][j-1]%mod)+(((j-i*3)*mi(j,mod-2)%mod)*dp[i][j-1]%mod))%mod;
}
}
int t;
cin>>t;
for(int times=1;times<=t;times++){
string s;
cin>>s;
string ss;
int num=0;
mp.clear();
for(int i=1;i<s.length();i+=2){
ss="";
ss+=s[i-1];
ss+=s[i];
if(mp.count(ss)){
num--;
}
else{
mp[ss]=1;
num++;
}
}
cout<<"Case #"<<times<<": "<<dp[num][sum]<<endl;
}
return 0;
}
|