牛客網NOIP賽前集訓營-提高組(第三場)A-管道維修
題目描述
在維修下水管道的過程中,發現一塊n×m的易堵區域。為了方便表示,將易堵區域第行第列的格子命名為格子。每次維修下水道時,其中有些格子是堵塞狀態,有些則是未堵塞狀態。維修需要分步進行:所有與長方形四周邊界相鄰的格子或者與未被堵塞的格子相鄰(四連通)的堵塞格子都可以在第1步內清理完畢。第k步過後,清理完畢的格子就變成未被堵塞的格子,這時與邊界或者未堵塞的格子相鄰的堵塞格子都會在第步內被清理完畢。依此類推,直到所有格子都變成未堵塞的格子。現在下水道內的情況未知,格子堵塞的概率為。不同格子之間堵塞的事件相互獨立。現在求每個格子平均需要到第幾步被清理完畢。一個格子如果一開始就是未堵塞的,算作在第步被清理完畢。
輸入描述:
第一行兩個個正整數和表示區域的大小。 接下來n行每行m個整數對表示格子被堵塞的概率是。
輸出描述:
行每行個整數表示格子平均在第幾步清理完畢。為了避免精度問題,如果平均在第步內被清理完畢,則輸出,其中表示關於的逆元,即到以內唯一一個和相乘後模等於的整數。
示例1 輸入
5 5 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2
輸出
500000004 500000004 500000004 500000004 500000004 500000004 781250006 781250006 781250006 500000004 500000004 781250006 665161138 781250006 500000004 500000004 781250006 781250006 781250006 500000004 500000004 500000004 500000004 500000004 500000004
說明
以左上的格子為例,如果這個格子是堵塞格子(發生概率為),由於它和邊界相鄰,一定可以在第輪就被清理完畢。如果它不是堵塞格子,按照定義在第輪清理完畢。所以平均它在第輪被清理完畢。的逆元是。其它與邊界不相鄰的格子有可能在第輪才被清理完畢,取決於它們旁邊的格子是否是堵塞的。
示例2 輸入
3 3 1 2 1 3 1 2 1 3 2 3 1 3 1 2 1 3 1 2
輸出
500000004 333333336 500000004 333333336 82304528 333333336 500000004 333333336 500000004
Solution
對於每一個計算期望先考慮樣例1 的計算方法 的計算方法也類似(具體數值也沒有算過) 可以發現一個規律:每層往外擴充套件都是分成兩種情況 1.這層全是堵塞的(概率為) 2.這層不是全部堵塞(概率為) 其中第一種情況是可以繼續擴充套件的 所以如果存在有一層,則可以直接跳出迴圈 後來維護每層的概率積有兩種方法 1.暴力,時間複雜度 2.字首積,時間複雜度,當然這個字首積是要斜著維護的,而且要維護兩個方向 這個字首積還有坑!!! 如果有的情況就會出現的慘狀,但是發現遇到可以直接跳出,於是又用同樣的方法維護了一遍的個數,如果存在就結束。 至於分數的運算很麻煩的問題…dalao告訴我們可以直接使用逆元進行運算,不會有任何問題
Code(然而不知道哪出bug了,94分…)
#include <cstdio>
#include <algorithm>
#define N 205
#define MOD 1000000007
typedef long long LL;
using namespace std;
int ll[N][N], rr[N][N];//ll,rr是0的個數字首和
LL lo[N][N], ro[N][N], val[N][N];//lo,ro是字首積,val是一個點的概率的逆元
LL qui_pow(LL x, int y) {
if (y == 1) return x;
LL t = qui_pow(x, y / 2);
if (y % 2 == 0) return t * t % MOD;
else return t * t % MOD * x % MOD;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
LL a, b;
scanf("%lld%lld", &a, &b);
val[i][j] = a * qui_pow(b, MOD - 2) % MOD;
}
}
for (int i = 0; i <= n + 1; ++i) {
for (int j = 0; j <= n + 1; ++j) {
if (i == 0 || j == 0) {
lo[i][j] = ro[i][j] = 1;//不能出現lo,ro為零的情況
}
}
}
for (int i = 1; i <= n; ++i) {//字首預處理
for (int j = 1; j <= m; ++j) {
if (val[i][j] == 0) {
ll[i][j] = ll[i - 1][j - 1] + 1;
rr[i][j] = rr[i - 1][j + 1] + 1;
}
else {
ll[i][j] = ll[i - 1][j - 1];
rr[i][j] = rr[i - 1][j + 1];
}
lo[i][j] = lo[i - 1][j - 1] * val[i][j] % MOD;
ro[i][j] = ro[i - 1][j + 1] * val[i][j] % MOD;
if (lo[i][j] == 0) lo[i][j] = 1;
if (ro[i][j] == 0) ro[i][j] = 1;//不能出現lo,ro為零的情況
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
int p = min(min(i, j), min(n - i + 1, m - j + 1));//最大層數
LL ans = 0, now = val[i][j];
for (int k = 1; k <= p; ++k) {
int cnt = ll[i + k - 1][j] - ll[i - 1][j - k]
+ ll[i][j + k - 1] - ll[i - k][j - 1]
+ rr[i - 1][j - k + 2] - rr[i - k + 1][j]
+ rr[i + k - 2][j + 1] - rr[i][j + k - 1];//0個數
LL pob = lo[i + k][j] * qui