Luogu P4204 神奇口袋 题解报告

题目传送门

【题目大意】

一个口袋里装了t种颜色的球,第i种颜色的球的数目为a[i],每次随机抽一个小球,然后再放d个这种颜色的小球进口袋。

给出n个要求,第x个抽出的球颜色为y,求满足条件的概率。

【思路分析】

抽出一个球颜色为i的概率设为f[i],球的总数为sum

在第k步时,$f[i]=frac{a[i]}{sum}$

那么在k+1步就有两种情况:

1.第k步抽中了颜色为i的球,那么此时概率为$frac{a[i]}{sum}*frac{a[i]+d}{sum+d}$

2.第k步没有抽中,那么此时概率为$(1-frac{a[i]}{sum})*frac{a[i]}{sum+d}$

所以第k+1步时,$f[i]=(1-frac{a[i]}{sum})*frac{a[i]}{sum+d}+frac{a[i]}{sum}*frac{a[i]+d}{sum+d}=frac{a[i]*(sum-a[i]+a[i]+d)}{sum*(sum+d)}=frac{a[i]}{sum}$

由此可得,在任意时刻抽到某一种颜色的小球的概率是不变的,始终为$frac{a[i]}{sum}$

如果这道题没有条件的话,到这里就可以完美解决了,但是我们还要考虑题目的条件。

这里有一个结论:要求中某一步要取的颜色出现的顺序对概率并没有影响。

假设现在的两个要求中的小球颜色分别为i,j

1.若i在前,概率$P1=frac{a[i]}{sum}*frac{a[j]}{sum+d}$

2.若j在前,概率$P2=frac{a[j]}{sum}*frac{a[i]}{sum+d}$

显然,$P1=P2=frac{a[i]*a[j]}{sum*(sum+d)}$,得证。

然后写代码的时候还要注意一点就是那个概率的数字可能会很大,所以要先分解质因数约分一下。

【代码实现】

 1 #include<bits/stdc++.h>
 2 #define go(i,a,b) for(register int i=a;i<=b;i++)
 3 #define back(i,a,b) for(register int i=a;i>=b;i--)
 4 #define ll long long
 5 using namespace std;
 6 int fr(){
 7     int w=0,q=1;
 8     char ch=getchar();
 9     while(ch>'9'||ch<'0'){
10         if(ch=='-') q=-1;
11         ch=getchar();
12     }
13     while(ch>='0'&&ch<='9')
14         w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
15     return w*q;
16 }
17 int prime[20002],num=0;
18 int t,n,d;
19 int a[1002],sum,ans1[20002],ans2[20002];
20 int A1[20002],A2[20002];
21 void Prime(){//找质数
22     int v[20002];
23     memset(v,0,sizeof(v));
24     go(i,2,20000){
25         if(!v[i]) v[i]=i,prime[++num]=i;
26         go(j,1,num){
27             if(i*prime[j]>20000||prime[j]>v[i]) break;
28             v[i*prime[j]]=prime[j];
29         }
30     }
31     return;
32 }
33 void yue(int *ans,int x){//约分
34     go(i,1,num){
35         if(x<prime[i])break;
36         while(x%prime[i]==0)
37             ans[i]++,x/=prime[i];
38     }
39     return;
40 }
41 void add(int *ans,int x,int &len){
42     go(i,1,len) ans[i]*=x;
43     go(i,1,len) ans[i+1]+=ans[i]/10,ans[i]%=10;
44     while(ans[len+1])
45         len++,ans[len+1]+=ans[len]/10,ans[len]%=10;
46     return;
47 }
48 void mul(int *ans,int *x,int &len){//把约分后的数乘起来
49     go(i,1,num)
50         while(x[i]) add(ans,prime[i],len),x[i]--;
51     return;
52 }
53 int main(){
54     t=fr();n=fr();d=fr();
55     go(i,1,t) a[i]=fr(),sum+=a[i];
56     Prime();
57     go(i,1,n){
58         int y=fr();y=fr();
59         if(!a[y]){printf("0/1");return 0;}
60         yue(ans1,a[y]);yue(ans2,sum);sum+=d;a[y]+=d;
61     }
62     go(i,1,num){
63         int cut=min(ans1[i],ans2[i]);
64         ans1[i]-=cut;ans2[i]-=cut;
65     }
66     A1[1]=A2[1]=1;int len1=1,len2=1;
67     mul(A1,ans1,len1);mul(A2,ans2,len2);
68     back(i,len1,1) printf("%d",A1[i]);
69     printf("/");
70     back(i,len2,1) printf("%d",A2[i]);
71     return 0;
72 }
代码戳这里
原文地址:https://www.cnblogs.com/THWZF/p/10699975.html