CF989E A Trance of Nightfall(概率+矩阵快速幂优化+倍增)
CF传送门
洛谷传送门
【题目分析】
在zxy大佬的讲解下终于懂了这道题的做法了qwq。。。
首先根据题意,出发点不一定在特殊点上,但第一次操作后,之后所有的操作都是在特殊点上,所以先考虑从线上出发的最大概率,再加一步即可得到从点出发的最大概率,二者取较大值即可。
记数组f[i][j][k]表示从i点走k步到j点的概率,所以转移方程就出来了:
然后发现这个形式其实就是矩阵乘法,所以可以利用矩阵快速幂优化,计算出走2^i步的概率。
但每次都进行一次快速幂的计算复杂度为O(n^3log(q)),所以继续优化。
因为我们只需要考虑最后到达t的最大概率,所以在进行矩阵乘法的时候很多位置都是没用的,所以考虑将初始矩阵简化为一个1*n的矩阵,表示走0步到达t的概率,显然只有base[t]=1,其他位置均为0。
然后将操作数进行二进制拆分进行左乘,因为初始矩阵只有1行,所以不管乘几次都只有一行,这样直接优化了一个n的复杂度。
从直线开始就是比从点开始少了一步(因为要先走到点上),所以就先处理从直线开始走的情况统计答案,最后再计算一次就可以得到从点开始走的概率。
考虑构造f[i][j][0],显然从i点走一步到达j点的概率为(1/(经过i点直线数)*(直线i,j上的点数)),根据这个构造即可。
然后就是各种小细节。。。
1.直线去重,这样可以避免进行重复计算。
2.将一个vector的值赋给另一个vector的时候加个传址符会快一点。
3.在计算f数组和base数组的时候如果f[i][j][k]或g[i]已经小于1e-6了,那么其实并没有必要继续计算下去了,因为精度太小反而可能会爆炸,而且题目也说了误差在1e-6以内即可,这样大大减少运行时间。
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=201;
const int MAXM=15;
int n,q;
int x[MAXN],y[MAXN];
int vis[MAXN],cnt[MAXN];
double ans;
double f[MAXN][MAXN][MAXM+1];
double Base[MAXN],zy[MAXN];
vector<int> point[MAXN][MAXN];
vector<pair<int,int> > line;
inline int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
bool check(int a,int b,int c){
return (y[b]-y[a])*(x[c]-x[a])==(y[c]-y[a])*(x[b]-x[a]);
}
int main(){
n=Read();
for(int i=1;i<=n;++i)
x[i]=Read(),y[i]=Read();
for(int i=1;i<=n;++i){
memset(vis,0,sizeof(vis));
for(int j=1;j<=n;++j){
if(i==j)
continue;
if(vis[j])
continue;
cnt[i]++;
for(int k=1;k<=n;++k){
if(check(i,j,k)){
point[i][j].push_back(k);
vis[k]=1;
}
}
line.push_back(make_pair(point[i][j][0],point[i][j][1]));
}
}
sort(line.begin(),line.end());
line.erase(unique(line.begin(),line.end()),line.end());
int siz1=line.size();
for(int i=0;i<siz1;++i){
vector<int> &vec=point[line[i].first][line[i].second];
int siz2=vec.size();
for(int j=0;j<siz2;++j){
for(int k=0;k<siz2;++k){
f[vec[j]][vec[k]][0]+=1.0/siz2*1.0;
}
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
f[i][j][0]/=cnt[i];
}
}
for(int i=1;i<=MAXM;++i){
for(int j=1;j<=n;++j){
for(int k=1;k<=n;++k){
if(f[j][k][i-1]>1e-6){
for(int t=1;t<=n;++t){
f[j][t][i]+=f[j][k][i-1]*f[k][t][i-1];
}
}
}
}
}
q=Read();
while(q--){
int t=Read(),step=Read()-1;
memset(Base,0,sizeof(Base));
Base[t]=1;
for(int i=0;i<=MAXM;++i){
if((1<<i)>step)
break;
if((1<<i)&step){
memset(zy,0,sizeof(zy));
for(int j=1;j<=n;++j){
if(Base[j]>1e-6){
for(int k=1;k<=n;++k){
zy[k]+=f[k][j][i]*Base[j];
}
}
}
memcpy(Base,zy,sizeof(zy));
}
}
ans=0.0;
int siz=line.size();
for(int i=0;i<siz;++i){
vector<int> &vec=point[line[i].first][line[i].second];
double tot=0.0;
int siz2=vec.size();
for(int j=0;j<siz2;++j){
tot+=Base[vec[j]];
}
tot/=1.0*siz2;
ans=max(ans,tot);
}
memset(zy,0,sizeof(zy));
for(int i=1;i<=n;++i){
if(Base[i]>1e-6){
for(int j=1;j<=n;++j){
zy[j]+=Base[i]*f[j][i][0];
}
}
}
memcpy(Base,zy,sizeof(zy));
for(int i=1;i<=n;++i)
ans=max(ans,Base[i]);
printf("%.10lf\n",ans);
}
return 0;
}