1. 程式人生 > >2018.11.08【CodeForces989】E. A Trance of Nightfall(矩陣快速冪)(倍增)

2018.11.08【CodeForces989】E. A Trance of Nightfall(矩陣快速冪)(倍增)

傳送門


解析:

考場上本來想寫倍增來著結果發現這個東西可以矩陣快速冪轉移,所以倍增陣列就用來優化矩陣快速冪了。。。(省去每次求出轉移矩陣的一個 O ( n ) O(n) ,過會看完優化的第二種就行了)

如果只有一次詢問,我們只需要求出每個點在跑了 m

m 次後以及每條線在跑了 m m 次後到達目標點的距離。這個顯然只需要把每個點走 m 1
m-1
次的概率 D P DP 出來計算直線最大,因為直線需要走一步到點上面,然後把每個點走 m m 次的概率求出來,所有點取最大。然後兩者取一下最大。

有一個地方需要讀者思考一下,為什麼不需要考慮線的交點?

很顯然的結論,考場上只卡了我30s就想出來了,因為幾個數的平均數肯定不大於它們當中的最大數,選擇交點一定不會比直接選擇概率最大的線優。

那麼怎麼計算每個點走 s t e p step 步達到 g o a l goal 的概率。考慮現在已經求出矩陣 A i A_i A i , u , v A_{i,u,v} 表示 u u i i 步後停留在節點 v v 的概率,我們只要知道 A 1 A_1 。就可以知道 A i + 1 A_{i+1} . A i + 1 , u , v = j = 1 n A i , u , j × A 1 , j , v A_{i+1,u,v}=\sum_{j=1}^{n}A_{i,u,j}\times A_{1,j,v}

然後發現這個東西其實就是矩陣乘法,於是可以矩陣快速冪優化一下。

然後就是每次都來一下 O ( n 3 log m ) O(n^3\log m) 的快速冪時間複雜度是會爆炸的。所以我們可以再優化一下。

第一種優化是基於 B S G S BSGS 的思想,我們只需要預處理所有 A k m A_{k\sqrt{m}} 的矩陣以及所有 ( 0 i m ) A i (0\leq i \leq \sqrt{m})A_i 就可以直接找到兩個矩陣做一次 O ( n 3 ) O(n^3) 的乘法就可以構造任意矩陣,優化效果 O ( n 3 log m ) > O ( n 3 ) O(n^3\log m)->O(n^3) ,沒測試,不知道能不能過,不過應該不可以,剛好卡複雜度上界啊。。。

第二種優化直接優化一個 O ( n ) O(n)
由於我們只需要求出到達 g o a l goal 的距離,所以構造一個只有一列的矩陣 g g ,其中 g g o a l = 1 g_{goal}=1 ,表示一步不走的時候只有 g o a l goal 到達 g o a l goal 的概率是 1 1 。用這個矩陣轉移出來任何時候都只有一列矩陣,矩陣乘法的複雜度就降為 O ( n 2 ) O(n^2) ,優化效果 O ( n 3 log m ) > O ( n 2 log m ) O(n^3\log m)->O(n^2\log m)

那麼考慮怎麼求出最初的矩陣 A 1 A_1 ?

首先我們需要處理出哪些點在同一條直線上面,這個用點斜式判一下就好了。
我們同時需要處理出經過一個點的有多少直線。同直線上的點相互到達的概率就是 1 / s i z e ( l i n e ) 1/size(line) ,其中 s i z e size 表示直線上的點數。

同時每個點向外的初始轉移需要除以 c n t u cnt_u ,其中 c n t u cnt_u 表示經過點 u u 的有多少直線。

然後我們就用倍增預處理出矩陣就好了


程式碼(由於很多該封裝的沒有封裝,所以很醜,但是也正因為這樣才跑的那麼快,所以不封裝有不封裝的好處):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	re bool f=0;
	while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return f?-num:num;
}

cs int N=202,logM=15;

int x[N],y[N],cnt[N];
vector<int> G[N][N];
double f[logM+1][N][N];
double g[N];
double tmp[N];
bool vis[N];
vector<pair<int,int> > line;

inline bool judge(int u,int v,int i){
	return (x[i]-x[v])*(y[v]-y[u])==(x[v]-x[u])*(y[i]-y[v]);
}
int n,q;
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i){
		x[i]=getint();
		y[i]=getint();
	}
	for(int re u=1;u<=n;++u){
		memset(vis,0,sizeof vis);
		for(int re v=1;v<=n;++v){
			if(u==v)continue;
			if(vis[v])continue;++cnt[u];
			for(int re i=1;i<=n;++i){
				if(judge(u,v,i))G[u][v].push_back(i),vis[i]=true;
			}
			line.push_back(make_pair(G[u][v][0],G[u][v][1]));
		}
	}
	
	sort(line.begin(),line.end());
	line.erase(unique(line.begin(),line.end()),line.end());
	
	for(int re kkk=0;kkk<line.size();++kkk){
		vector<int> &vec=G[line[kkk].first][line[kkk].second];
		for(int re i=0;i<vec.size();++i)
		for(int re j=0;j