Link
设\(p_i=r_i+g_i,P=\operatorname{lcm}(p_i)\),很显然\(X|2019!\),因此题意等价于\([0,P)\cap\mathbb N\)内随机。
同时我们可以将题意转化为求通过前\(i\)扇门的概率,通过差分即可得到被第\(i\)扇门阻挡的概率。
能通过第\(i\)扇门当且仅当\(t+x_i\bmod p_i\in[r_i,p_i)\),如果两个同余方程的\(p_i\)互质,那么这两个同余方程组合并之后解集的大小就是两个同余方程组解集大小的积。
如果\(p_i|p_j\),那么我们也可以暴力合并两个同余方程组的解集,可以利用压位或\(\text{bitset}\)优化。
枚举\(a\in[0,X)\),对所有开始时间为\(t=kX+a(k\in\mathbb N)\)的求出答案。
不难发现\(t\bmod p_i\)是否在\([r_i,p_i)\)内仅和\(k\bmod\frac{p_i}{\gcd(p_i,X)}\)有关。
令\(X=2520\),这样保证了\(\omega(\frac{p_i}{\gcd(p_i,X)})=1\)
因此我们可以再枚举\(k\in[0,\frac{p_i}{\gcd(p_i,X)})\)中的合法值,然后就只需要考虑\(p_i\perp q_i\)和\(p_i|p_j\)情况下的合并了。
时间复杂度为\(O(nX)\)。
#include<cstdio>
#include<vector>
#include<numeric>
using u64=unsigned long long;
using u128=unsigned __int128;
const int N=507,M=107,X=2520;
int n,x[N],g[N];double ans[N],v[N];u128 s[N][M],t;
std::vector<int>m[M];
int ct(u128 x){return __builtin_popcountll(x&(u64)-1)+__builtin_popcountll(x>>64);}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
int d,r;
scanf("%d%d%d",x+i,&r,g+i),g[i]+=r,d=g[i]/std::gcd(X,g[i]),d&1||(d=8),d%3||(d=9),m[d].push_back(i);
for(int j=0;j<g[i];++j) for(int k=0;k<d;++k) s[i][j]|=(u128)((j+k*X)%g[i]>=r)<<k;
}
for(int a=0;a<X;++a)
{
std::fill(v,v+n+1,1);
for(int d=1;d<=100;++d)
{
int n=0,l=d;u128 t=~((u128)-1<<d);
for(int p:m[d]) if(n=ct(t&=s[p][(a+x[p])%g[p]]),v[p]*=(double)n/l,!(l=n)) break;
}
for(int i=0;i<=n;++i) v[i]*=(i? v[i-1]:1),ans[i]+=v[i];
}
for(int i=0;i<=n;++i) printf("%.9lf\n",(ans[i]-ans[i+1])/X);
}