2018.11.08【CodeForces989】E. A Trance of Nightfall(矩陣快速冪)(倍增)
傳送門
解析:
考場上本來想寫倍增來著結果發現這個東西可以矩陣快速冪轉移,所以倍增陣列就用來優化矩陣快速冪了。。。(省去每次求出轉移矩陣的一個 ,過會看完優化的第二種就行了)
如果只有一次詢問,我們只需要求出每個點在跑了 次後以及每條線在跑了 次後到達目標點的距離。這個顯然只需要把每個點走 次的概率 出來計算直線最大,因為直線需要走一步到點上面,然後把每個點走 次的概率求出來,所有點取最大。然後兩者取一下最大。
有一個地方需要讀者思考一下,為什麼不需要考慮線的交點?
很顯然的結論,考場上只卡了我30s就想出來了,因為幾個數的平均數肯定不大於它們當中的最大數,選擇交點一定不會比直接選擇概率最大的線優。
那麼怎麼計算每個點走 步達到 的概率。考慮現在已經求出矩陣 , 表示 走 步後停留在節點 的概率,我們只要知道 。就可以知道 .
然後發現這個東西其實就是矩陣乘法,於是可以矩陣快速冪優化一下。
然後就是每次都來一下 的快速冪時間複雜度是會爆炸的。所以我們可以再優化一下。
第一種優化是基於 的思想,我們只需要預處理所有 的矩陣以及所有 就可以直接找到兩個矩陣做一次 的乘法就可以構造任意矩陣,優化效果 ,沒測試,不知道能不能過,不過應該不可以,剛好卡複雜度上界啊。。。
第二種優化直接優化一個
由於我們只需要求出到達
的距離,所以構造一個只有一列的矩陣
,其中
,表示一步不走的時候只有
到達
的概率是
。用這個矩陣轉移出來任何時候都只有一列矩陣,矩陣乘法的複雜度就降為
,優化效果
那麼考慮怎麼求出最初的矩陣 ?
首先我們需要處理出哪些點在同一條直線上面,這個用點斜式判一下就好了。
我們同時需要處理出經過一個點的有多少直線。同直線上的點相互到達的概率就是
,其中
表示直線上的點數。
同時每個點向外的初始轉移需要除以 ,其中 表示經過點 的有多少直線。
然後我們就用倍增預處理出矩陣就好了
程式碼(由於很多該封裝的沒有封裝,所以很醜,但是也正因為這樣才跑的那麼快,所以不封裝有不封裝的好處):
#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