把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

几种特殊情况

假设起始时刻为\(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\times3^2\times5\times7=2520\),我们枚举模\(P\)的余数\(t\),那么所有的时间都可以被表示成\(kP+t\)的形式。

由于\(kP+t\equiv 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\n",(f[i-1]-f[i])/P);return 0;//枚举模P余数,最终答案要除以P
}
posted @ 2021-06-17 17:52  TheLostWeak  阅读(152)  评论(0编辑  收藏  举报