题目
根据题意,只有刚开始所在的点可能不是给定的\(n\)个点,之后所有的位置一定都在给定的点集中了;我们大力求一下\(P_{i,j}\)表示从点\(i\)移动到点\(j\)的概率,这个随便暴力一下就好了,由于询问的终点是给定的,于是我们利用求出来的\(P_{i,j}\)建一张反图
接下来不妨考虑一下起始点的位置,我们大力猜想这个起始点不可能是两条直线的交点,感性理解一下发现这个结论非常正确;在交点上相当于对各条直线上的答案取了一个加权平均值,这个平均值一定小于答案最大的那条直线,我们把起始点放在那条直线上一定比在交点上更优。
于是起始点只可能有两种情况,可能是给定点集中的某个点,也可能在给定点集形成的一条直线上且不是任意两条直线的交点;不难发现第二种情况包含第一种情况,我们只需要考虑第二种情况即可。
由于直线条数不超过\(O(n^2)\)条,于是我们可以暴力每一条直线,并处理出每一条直线上点有哪些。一条直线的答案就是上面每一个点到\(t\)的概率除以直线上点的个数。
由于我们建了反图,所以转化成了\(t\)到上面每一个点的概率,我们现在要求的就是这个东西了;不难发现这是一个简单dp,我们可以通过矩阵来优化这个dp,但是直接上是\(O(qn^3\log m)\)的,这看起来不是很科学。
这就是一个简单的套路了,两个矩阵相乘是\(O(n^3)\)的,但是向量乘矩阵却是\(O(n^2)\)的,我们求出最终的转移矩阵也只是为了和一个向量相乘,所以我们只保留\(2\)的次幂的矩阵,拿着向量去乘这些矩阵就好了,这样一组询问的复杂度就只有\(O(n^2\log m)\)了。
复杂度是\(O(n^3+qn^2\log m)\),代码
#include
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
const int maxn=205;
int n,f[maxn],vis[maxn],Q,ma[maxn][maxn],num;
struct pt{int x,y;}p[maxn];
struct Mat{double a[maxn][maxn];}pw[14];
struct Vector{double d[maxn];}ans;
std::vector line[maxn*maxn];
inline int operator*(const pt &A,const pt &B) {return A.x*B.y-A.y*B.x;}
inline pt operator-(const pt &A,const pt &B) {return (pt){A.x-B.x,A.y-B.y};}
inline Mat operator*(const Mat &A,const Mat &B) {
Mat C;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=n;j++) C.a[i][j]=0;
for(re int k=1;k<=n;k++)
for(re int i=1;i<=n;i++)
for(re int j=1;j<=n;j++) C.a[i][j]+=A.a[i][k]*B.a[k][j];
return C;
}
inline Vector operator*(const Vector &A,const Mat &B) {
Vector C;for(re int i=1;i<=n;i++) C.d[i]=0;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=n;j++) C.d[j]+=A.d[i]*B.a[i][j];
return C;
}
inline void calc(int t,int m) {
for(re int i=1;i<=n;i++)ans.d[i]=0;ans.d[t]=1;
for(re int i=13;i>=0;--i)if(m>>i&1) ans=ans*pw[i];
}
inline void work() {
for(re int i=0;i