1. 程式人生 > >牛客網NOIP賽前集訓營-提高組(第三場)A-管道維修

牛客網NOIP賽前集訓營-提高組(第三場)A-管道維修

題目描述

在維修下水管道的過程中,發現一塊n×m的易堵區域。為了方便表示,將易堵區域第ii行第jj列的格子命名為格子(i,j)(1in,1jm)(i,j)(1≤i≤n,1≤j≤m)。每次維修下水道時,其中有些格子是堵塞狀態,有些則是未堵塞狀態。維修需要分步進行:所有與n×mn×m長方形四周邊界相鄰的格子或者與未被堵塞的格子相鄰(四連通)的堵塞格子都可以在第1步內清理完畢。第k步過後,清理完畢的格子就變成未被堵塞的格子,這時與邊界或者未堵塞的格子相鄰的堵塞格子都會在第k+1k+1步內被清理完畢。依此類推,直到所有格子都變成未堵塞的格子。現在下水道內的情況未知,格子(

i,j)(i,j)堵塞的概率為p(i,j)p(i,j)。不同格子之間堵塞的事件相互獨立。現在求每個格子平均需要到第幾步被清理完畢。一個格子如果一開始就是未堵塞的,算作在第00步被清理完畢。

輸入描述:

第一行兩個個正整數nnmm表示區域的大小。(1n,m200)(1≤n,m≤200) 接下來n行每行m個整數對a,ba,b表示格子被堵塞的概率是a/ba/b(0ab,1b<109+7)(0≤a≤b,1≤b<10^9+7)

輸出描述:

nn行每行mm個整數表示格子平均在第幾步清理完畢。為了避免精度問題,如果平均在第c

/dc/d步內被清理完畢,則輸出cd1(mod109+7)c⋅d−1(mod10^9+7),其中d1d−1表示dd關於109+710^9+7的逆元,即00109+610^9+6以內唯一一個和dd相乘後模109+710^9+7等於11的整數。

示例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

說明

以左上的格子為例,如果這個格子是堵塞格子(發生概率為1/21/2),由於它和邊界相鄰,一定可以在第11輪就被清理完畢。如果它不是堵塞格子,按照定義在第00輪清理完畢。所以平均它在第1/21/2輪被清理完畢。22的逆元是500000004500000004。其它與邊界不相鄰的格子有可能在第22輪才被清理完畢,取決於它們旁邊的格子是否是堵塞的。

示例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

對於每一個(i,j)(i,j)計算期望先考慮樣例1 (2,2)(2,2)的計算方法12×0+12×1516×1+12×116×2=1732\frac{1}{2}\times 0+\frac{1}{2}\times \frac{15}{16}\times 1+\frac{1}{2}\times \frac{1}{16}\times 2=\frac{17}{32} 17×32117×321000000005781250006(mod  1000000007)17\times 32^{-1}\equiv 17 \times 32^{1000000005}\equiv 781250006(\mod1000000007) (3,3)(3,3)的計算方法也類似12×0+12×1516×1+12×116×255256×2+12×116×1256×3\frac{1}{2}\times 0+\frac{1}{2}\times \frac{15}{16}\times 1+\frac{1}{2}\times\frac{1}{16}\times\frac{255}{256}\times 2+\frac{1}{2}\times\frac{1}{16}\times\frac{1}{256}\times3(具體數值也沒有算過) 可以發現一個規律:每層往外擴充套件都是分成兩種情況 1.這層全是堵塞的(概率為ab\prod\frac{a}{b}) 2.這層不是全部堵塞(概率為1ab1-\prod\frac{a}{b}) 其中第一種情況是可以繼續擴充套件的 所以如果存在有一層a=0\exist a=0,則ab=0\prod\frac{a}{b}=0可以直接跳出迴圈 後來維護每層的概率積有兩種方法 1.暴力,時間複雜度θ(n4)\theta(n^4) 2.字首積,時間複雜度θ(n3)\theta(n^3),當然這個字首積是要斜著維護的,而且要維護兩個方向 direction 這個字首積還有坑!!! 如果有00的情況就會出現00\frac{0}{0}的慘狀,但是發現遇到00可以直接跳出,於是又用同樣的方法維護了一遍00的個數,如果存在00就結束。 至於分數的運算很麻煩的問題…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