P6261 [ICPC 2019 WF] Traffic Blights
题意
给定 \(n\) 个红绿灯,其中周期,\(r_i\) 秒红,\(g_i\) 秒绿。随机一个数从 \(2019!\) 中开始从 \(0\) 开始向右走,问第 \(i\) 个灯会停下的概率和全通过的概率。
\(n\leq 500,x\leq 10^5,r+g\leq 100\)
思路
发现直接枚举循环节和位置判断合法,时间复杂度 \(O(n\cdot\mathrm{lcm}(r_i+g_i))\)。我们考虑周期的性质,发现对于一个 \(r_i+g_i\perp r_j+g_j\),那么它们的循环节一定不会重复,我们可以直接枚举每一个然后将它们的结果乘起来。那么对于 \(r_i+g_i|r_j+g_j\),则我们发现可以直接将其补全,这样对 \(\mathrm{lcm}\) 的大小不会有办法。我们可以枚举一个周期 \(T\),然后对于每一个 \(T\) 里的大小去考虑,那么 \(r_i+g_i\rightarrow \gcd(r_i+g_i,T)\),时间复杂度 \(O(Tn\max((r_i+g_i)^{'}))\)。我们首先需要利用互质的性质,发现 \(r+g\) 很小,那么我们可以考虑将 \(10\) 以内的所有质数加进 \(T\) 里,即 \(2^6+3^4+5^2+7^2=6350400\),这样所有的数都互质了。但是这样发现时间还是爆炸,但是发现我们还可以利用那个倍数,将 \(635400\) 开根号变为 \(2520\)。这样子的话我们就可以将 \(2,4\) 置为 \(8\),将 \(3\) 置为 \(9\),那么复杂度就不会变化了。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long ll;
const int MAXN=505;
const int MAXW=105;
const int T=2520;
int n,x[MAXN],w[MAXN],r[MAXN],g[MAXN];
double ans[MAXN];
bool vs[MAXN][MAXW],fw[MAXW][MAXW];
void Do(int v){
memset(fw,false,sizeof fw);
double val=1;
ans[0]++;
rep(i,1,n){
int fm=0,fz=0;
rep(j,0,r[i]-1){
if(!fw[r[i]][j]){
fm++;
if(vs[i][(v+j%g[i]*T)%w[i]]){
fz++;
}else{
fw[r[i]][j]=true;
}
}
}
if(!fz){
break;
}
val*=fz*1.0/fm;
ans[i]+=val;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
rep(i,1,n){
cin>>x[i]>>r[i]>>g[i];
w[i]=r[i]+g[i];
x[i]%=w[i];
rep(j,r[i],w[i]-1){
vs[i][(j-x[i]+w[i])%w[i]]=true;
}
}
rep(i,1,n){
r[i]=g[i]=w[i]/__gcd(w[i],T);
if(g[i]==2||g[i]==4){
r[i]=8;
}else if(g[i]==3){
r[i]=9;
}
}
rep(i,0,T-1){
Do(i);
}
rep(i,0,n){
ans[i]/=T;
}
rep(i,1,n+1){
cout<<fixed<<setprecision(10)<<ans[i-1]-ans[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号