链接:https://ac.nowcoder.com/acm/contest/368/C
来源:牛客网
题目描述
现在一共有n天,第i天如果有流星雨的话,会有wiwi颗流星雨。
第i天有流星雨的概率是pipi。
如果第一天有流星雨了,那么第二天有流星雨的可能性是p2+Pp2+P,否则是p2p2。相应的,如果第i−1 (i≥2)i−1 (i≥2)天有流星雨,第i天有流星雨的可能性是pi+Ppi+P,否则是pipi。
求n天后,流星雨颗数的期望。
输入描述:
第一行三个整数,n,a,b,其中n为天数,P=abP=ab
第二行n个整数wiwi。
接下来n行,每行两个整数,x,y,第i+2行表示第i天有流星雨的概率pi=xypi=xy。
1≤n≤105, 1≤a,b,x,y,wi≤109, pi+P≤1.01≤n≤105, 1≤a,b,x,y,wi≤109, pi+P≤1.0
输出描述:
一行一个整数,为答案对109+7109+7 取模的结果。
即设答案化为最简分式后的形式为abab,其中a和b互质。输出整数 x 使得bx≡a(mod 109+7)bx≡a(mod 109+7)且0≤x<109+70≤x<109+7。可以证明这样的整数x是唯一的。
示例1
输入
2 1 3
1 1
1 2
1 2
输出
166666669
说明
第一天有流星雨第二天也有流星雨的概率是12×(12+13)12×(12+13),然后乘以流星雨的颗数2
第一天有流星雨第二天没有流星雨的概率是12×1612×16,乘以颗数1
第一天没有,第二天有的概率12×1212×12,乘以颗数1
第一天没有,第二天也没有的概率12×1212×12,乘以颗数0。
所以流星雨颗数的期望是7676
示例2
输入
3 1 5
1 1 2
1 2
1 4
2 3
输出
763333341
这题比较小白的我用dfs去写,准备超时,于是:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll mod=1e9+7;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0);
for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int n;
ll a,b;
ll ansfz,ansfm;
ll pfz[maxn],pfm[maxn],num[maxn];
ll dp[maxn][2];
struct node
{
ll fz,fm;
}dp[maxn][2];
void cal(ll &x,ll &y,ll a,ll b,ll a1,ll b1)//相加
{
if(a==0)
{
x=a1,y=b1;
return ;
}
ll fz=(a*b1%mod+a1*b%mod)%mod;
ll fm=b*b1%mod;
ll d=gcd(fz,fm);
if(d==0)
{
x=fz;
y=fm;
return ;
}
x=fz/d;
y=fm/d;
}
void cal1(ll &x,ll &y,ll a,ll b,ll a1,ll b1)//相乘
{
ll fz=a*a1%mod;
ll fm=b*b1%mod;
ll d=gcd(fz,fm);
if(d==0)
{
x=fz;
y=fm;
return ;
}
x=fz/d;
y=fm/d;
}
void dfs(int i,int pre,ll fz,ll fm,ll sum)
{
// printf("i:%d\n",i);
if(i==n+1)
{
ll f1,f2;
cal1(f1,f2,sum,1,fz,fm);
cal(ansfz,ansfm,ansfz,ansfm,f1,f2);//相加
return ;
}
if(pre==0)
{
ll f1,f2;
cal1(f1,f2,fz,fm,pfz[i],pfm[i]);
dfs(i+1,1,f1,f2,sum+num[i]);
cal1(f1,f2,fz,fm,pfm[i]-pfz[i],pfm[i]);
dfs(i+1,0,f1,f2,sum);
}
else
{
ll f1,f2;
ll d1,d2;
cal(d1,d2,a,b,pfz[i],pfm[i]);
cal1(f1,f2,fz,fm,d1,d2);
dfs(i+1,1,f1,f2,sum+num[i]);
cal1(f1,f2,fz,fm,d2-d1,d2);
dfs(i+1,0,f1,f2,sum);
}
}
int main()
{
cin>>n>>a>>b;
for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&pfz[i],&pfm[i]);
dfs(1,0,1,1,0);
cout<<ansfz*powmod(ansfm,mod-2)%mod<<endl;
// cout<<(ansfz+mod)/(ansfm%mod)<<endl;
}
很显然是超时的,看了其他人的题解后学到了一些新知识:
1、概率可以用逆元去解决处理,无需自己写一个函数处理分数的相加相减,p1是下雨,逆元中(1-p1+mod)%mod也是代表不下雨;
2、该题期望有两种算法:总概率p*总数量num;也可以当前i下雨的概率pi*num[i],然后求和。
有了以上知识就可以轻松写出代码了,由于自己菜,不懂这些,当时就是不会写,现在懂了~
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
const ll mod=1e9+7;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0);
for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll num[maxn];
ll d[maxn][2];
int main()
{
int n;
ll a,b;
cin>>n>>a>>b;
ll p=a*powmod(b,mod-2)%mod;
for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
ll ans=0;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&a,&b);
if(i==1)
{
ll p1=a*powmod(b,mod-2)%mod;
d[1][1]=p1;
d[1][0]=(1-p1+mod)%mod;
ans=p1*num[1]%mod;
continue;
}
ll p1=a*powmod(b,mod-2)%mod;//下
ll p0=(1-p1+mod)%mod;
ll p2=(p1+p)%mod;//下
ll p3=(1-p2+mod)%mod;
d[i][0]=(d[i-1][0]*p0%mod+d[i-1][1]*p3%mod)%mod;
d[i][1]=(d[i-1][0]*p1%mod)+(d[i-1][1]*p2%mod)%mod;
ans=(ans+d[i][1]*num[i]%mod)%mod;
}
cout<<ans<<endl;
}