IOI2021集训队作业147FK. Traffic Blights
一个数轴上有些红绿灯,它们距离原点\(pos_i\),一开始刚好为红,以\(red_i,green_i\)为周期红绿闪烁。
有个车从原点随机时间出发。对于每个\(i\),车第一次遇到红灯在\(i\)的概率。
\(n\le 500,1\le red_i+green_i\le 100\)
设\(x\)为出发时间。对于每个灯,可以列出个这样的方程:\(x\equiv a_i\pmod {p_i}\),\(p_i=red_i+green_i\),\(a_i\)有多个(看做有个表\(c_{i,j}\),如果\(c_{i,j}=1\)则\(a_j\)出现)。
问题变为:有多少个方程组满足有解。
如果\(p_i\)互质,直接乘起来即可。
如果\(p_i\)互为倍数关系,可以把小的那个暴力扩张它的表(即\(c_{i}\)),然后把它们的表并起来。
更一般的情况:考虑钦定一个整数\(M\),枚举\(r=x\mod M\),把方程改写为\(r+Mt\equiv a\pmod p\)。推一下:
现在用\(t\)来替代\(x\)。可以发现周期是\(\frac{p}{\gcd(M,p)}\)。当\(M=LCM(1,2,\dots,\sqrt V)\)时,\(\frac{p}{\gcd(M,p)}\)得到的东西都是质数的幂次的形式。这题中\(M=2520\)。
于是枚举\(r\),改写方程组,得到了一堆要么互质要么互为倍数的方程组,然后就容易处理了。
时间\(O(n*2520*V)\)。
PS:如果直接按照上面写,会发现它挂了(
注意这一步\(Mt\equiv a-r\pmod p\to \gcd(M,p)t\equiv a-r \pmod p\)。
如果只是在意解的个数,这样是没问题的。问题在于,两个方程中的\(t\)不是同一个东西。
解决方法:直接枚举\(t\in [0,\frac{p}{\gcd(M,p)})\),看是否\((r+Mt)\mod p\)在表中。
using namespace std;
#include <bits/stdc++.h>
#define N 505
#define ll long long
#define db long double
int gcd(int a,int b){
int k;
while (b)
k=a%b,a=b,b=k;
return a;
}
const int M=2520;
const int V=100;
int n;
int c[N][N];
int p[N];
int cnt[N];
db f[N],ans[N];
int buc[N][N],ori[N];
int ex(int x){
if (x%2==0) return 8;
if (x%3==0) return 9;
return x;
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i){
int pos,red,gre;
scanf("%d%d%d",&pos,&red,&gre);
p[i]=red+gre;
for (int j=red;j<p[i];++j)
c[i][((j-pos)%p[i]+p[i])%p[i]]=1;
}
db tot,rem;
for (int r=0;r<M;++r){
tot=1;
for (int i=1;i<=V;++i)
if (ex(i)==i){
tot*=i;
cnt[i]=i;
for (int j=0;j<i;++j)
buc[i][j]=1;
}
rem=tot;
for (int i=1;i<=n;++i){
int g=gcd(M,p[i]),p_=p[i]/g;
memset(ori,0,sizeof(int)*p_);
for (int j=0;j<p_;++j){
int a=r+j*M/*g*/;
if (c[i][a%p[i]])
ori[j%p_]=1;
}
// static int _ori[N];
// memset(_ori,0,sizeof(int)*p_);
// for (int j=0;j<p[i];++j)
// if (c[i][j] && (j-r)%g==0)
// _ori[((j-r)/g%p_+p_)%p_]=1;
// for (int j=0;j<p_;++j)
// if (ori[j]!=_ori[j])
// printf("fuck\n");
int e=ex(p_);
for (int j=0;j<e;++j)
if (ori[j%p_]==0 && buc[e][j]){
buc[e][j]=0;
rem=rem/cnt[e]*(cnt[e]-1);
cnt[e]--;
}
f[i]+=rem/tot;
}
}
ans[0]=1;
for (int i=1;i<=n;++i)
ans[i]=f[i]/M;
for (int i=0;i<=n;++i)
printf("%.9lf\n",(double)(ans[i]-ans[i+1]));
return 0;
}