题目传送门
题意: 有k种球,每天等概率随机产生一种球,并且收集它。对于q次询问,每次询问一个p,问你至少多少天,你把k种球全部收集到的概率大于等于 p 2000 \frac {p}{2000} 2000p 。( k , q , p < = 1000 k,q,p<=1000 k,q,p<=1000)
思路: 我们设 f [ i ] [ j ] f[i][j] f[i][j]为前 i i i天收集了 j j j种蛋的概率,那么 f [ i ] [ j ] f[i][j] f[i][j]就有两种方式转移:
- [ i ≥ j ] : f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + ( k − j + 1 ) / k [i≥j]:f[i][j]=f[i-1][j-1]+(k-j+1)/k [i≥j]:f[i][j]=f[i−1][j−1]+(k−j+1)/k
- [ i − 1 ≥ j ] : f [ i ] [ j ] = f [ i − 1 ] [ j ] + j / k [i-1≥j]:f[i][j]=f[i-1][j]+j/k [i−1≥j]:f[i][j]=f[i−1][j]+j/k
因为 f [ i ] [ j ] f[i][j] f[i][j]只与 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j]和 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i−1][j−1]有关,即只与上一层有关,所以我们可以考虑滚动数组优化掉一维.(虽然强行开二维也能过,但是优化后的时空性能都明显优于优化前)
我们用ans[i]表示当p=i时,需要的最小天数。因为 p p p最大是1000,那么当我们将ans[1000]处理完就可以退出预处理了。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
#define int long long
#define lowbit(x) x&-x
#define pii pair<int,int>
#define ull unsigned long long
#define pdd pair<double,double>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read()
{
int x=0,f=1;
char ch=gc();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=gc();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=gc();
}
return x*f;
}
using namespace std;
const int N=3e6+666;
const int inf=0x3f3f3f3f;
const int mod=998244353;
const double eps=1e-7;
const double PI=acos(-1);
double f[11111];
int ans[N],cnt=0;
signed main()
{
int k,q;
cin>>k>>q;
f[0]=1;
for(int i=1;cnt<=1000;i++)
{
for(int j=min(i,k);j>=1;j--)
f[j]=(f[j-1]*(k-(j-1))+f[j]*j)/k;
while(i>=k&&f[k]*2000>=cnt)
ans[cnt++]=i;
f[0]=0;
}
while(q--)
{
int p;cin>>p;
cout<<ans[p]<<endl;
}
return 0;
}
}