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

P5323 [BJOI2019] 光线 题目分析

题意

当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收。

设对于任意 \(x\),有 \(x \times a_i\%\) 单位的光会穿过它,有 \(x \times b_i\%\) 的会被反射回去。
现在 \(n\) 层玻璃叠在一起,有 \(1\) 单位的光打到第 \(1\) 层玻璃上,那么有多少单位的光能穿过所有 \(n\) 层玻璃呢?

对于 \(100\%\) 的数据,\(n\le 5\times 10^5\)\(1\le a_i \le 100\)\(0\le b_i \le 99\)\(1\le a_i+b_i \le 100\)

分析

看到这里,你要想到对于第 \(i\) 层透下去的可能从上而来,也可能是从下而来。

于是设 \(f_i\) 表示第 \(i\) 层玻璃光透下去的光照,\(g_i\) 表示从第 \(i\) 层玻璃上向上走的光照。

那么显然:

\[\begin{aligned} f_i=f_{i-1}\cdot a_i + g_{i + 1}\cdot b_i\\ g_i=f_{i-1}\cdot b_i + g_{i+1}\cdot a_i \end{aligned} \]

目标是求 \(f_n\),那么我们要么老老实实高斯消元(能吗?),要么代入解方程。

有一些边界信息:\(f_n=f_{n-1}a_i,g_n=f_{n-1}b_i,g_{n+1}=0\)

\(F_i=\frac{f_i}{f_{i-1}},G_i=\frac{g_i}{f_{i-1}}\)(这是因为我们要消掉 \(f_{i-1}\),才能实现倒序递推)。

代入上式并经过一些代数化简可以得到:

\[\begin{aligned} F_i=\frac{a_i}{1-G_{i+1}b_i}\\ G_i=b_i+G_{i+1}F_i\cdot a_i \end{aligned} \]

从后往前推就可以了,最后令 \(f_0=1\),求 \(f_n=f_0\prod_{i=1}^nF_i\) 即可。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <stdlib.h>
#include <cstdio>
#include <vector>
#define int long long
#define N 500005
using namespace std;
const int mod = 1e9 + 7;
int qpow(int a,int b) {
	int res = 1;
	while(b){
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
int n,a[N],b[N],F[N],G[N];
signed main(){
	cin >> n;
	for (int i = 1;i <= n;i ++) scanf("%lld%lld",&a[i],&b[i]);
	if (n == 1) return cout << a[1] * qpow(100,mod - 2) % mod,0;
	int ans = 1,_100 = qpow(100,mod - 2);
	for (int i = 1;i <= n;i ++) a[i] = a[i] * _100 % mod,b[i] = b[i] * _100 % mod;
	F[n] = a[n],G[n] = b[n];
	for (int i = n - 1;i;i --) {
		F[i] = a[i] * qpow((1 - G[i + 1] * b[i] % mod + mod),mod - 2) % mod;
		G[i] = b[i] + G[i + 1] * a[i] % mod * F[i] % mod;
	} 
	for (int i = 1;i <= n;i ++) ans = ans * F[i] % mod;
	cout << ans;
	return 0;
}
posted @ 2026-07-15 16:06  high_skyy  阅读(7)  评论(0)    收藏  举报
动态线条
动态线条end
浏览器标题切换
浏览器标题切换end
💬 加载中……