【洛谷6261】[ICPC2019 WF] Traffic Blights(数论)

点此看题面

  • 一条路上有(n)个红绿灯,第(i)个红绿灯在(x_i)位置上。
  • (0)时刻开始计算,第(i)个红绿灯会红(r_i)秒,绿(g_i)秒,如是循环。
  • 在某个随机时刻出发,对于每个红绿灯求出最早在它停下的概率,并求出能走到最后的概率。
  • (nle500,r_i+g_ile100)

几种特殊情况

假设起始时刻为(s),令(p_i=r_i+g_i),那么不能通过第(i)个红绿灯就意味着((s+x_i)\% p_i<r_i)

(f_i)表示在第(i)个红绿灯不停下的概率,那么停在第(i)个红绿灯的概率就是(f_{i-1}-f_i)

如果(p_i)两两互质,概率可以直接相乘。

如果(p_i)两两相等,可以直接对覆盖的区间求交。实际上,如果(p_i)两两具有倍数关系,也可以把区间赋值若干份,使得所有的(p_i)都被调成一样。

如果(p_i)要么互质,要么成倍数关系,可以对于每个成倍数关系的部分按照第二种情况的方式求解,然后按照第一种情况把每一部分概率相乘。

现在我们希望把所有情况都化乘这里的第三种情况。

巧妙的数学手法

(P=2^3 imes3^2 imes5 imes7=2520),我们枚举模(P)的余数(t),那么所有的时间都可以被表示成(kP+t)的形式。

由于(kP+tequiv r(mod p_i))的模数可以被替换为(frac {p_i}{gcd(P,p_i)}),而(100)以内的数除以与(2520)(gcd)之后最多剩余一种质因子,满足了所有(p_i')要么互质要么成倍数关系,就可以套用上面的做法了。

代码:(O(2520np))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500
#define V 100
#define P 2520
#define DB long double
using namespace std;
int n,p[N+5],tg[N+5][V+5];DB f[N+5];
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
I int T(CI x) {return x==2||x==4?8:(x==3?9:x);}//把x调到可能的最大倍数
DB k[V+5];int vis[V+5][V+5];I void Solve(CI t)//余数为t时
{
	RI i,j;for(i=1;i<=V;++i) k[i]=1;memset(vis,0,sizeof(vis));//初始化
	RI x,w;DB o=1;for(++f[0],i=1;i<=n&&o>1e-12;f[i++]+=(o*=k[w]))
		for(o/=k[w=T(p[i]/gcd(P,p[i]))],j=0;j^w;++j) tg[i][x=(j*P+t)%p[i]]&&!vis[w][j]&&(k[w]-=1.0/w,vis[w][j]=1);//枚举时间jP+t
}
int main()
{
	RI i,j,x,y;for(scanf("%d",&n),i=1;i<=n;++i)
		for(scanf("%d%d%d",&x,&y,p+i),x%=(p[i]+=y),j=0;j^y;++j) tg[i][(j-x+p[i])%p[i]]=1;//给红灯对应余数打标记
	for(i=0;i^P;++i) Solve(i);for(i=1;i<=n+1;++i) printf("%.12Lf
",(f[i-1]-f[i])/P);return 0;//枚举模P余数,最终答案要除以P
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6261.html