【CF989E】A Trance of Nightfall

题目

根据题意,只有刚开始所在的点可能不是给定的\(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<bits/stdc++.h>
#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<int> 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<line[num].size();++i)
        for(re int j=0;j<line[num].size();++j)ma[line[num][i]][line[num][j]]=1;
}
int main() {
    scanf("%d",&n);
    for(re int i=1;i<=n;i++)scanf("%d%d",&p[i].x,&p[i].y);
    for(re int i=1;i<=n;i++) {
        int Line_num=0;double nw=0;
        for(re int j=1;j<=n;j++) {
            if(j==i||vis[j]) continue;
            int tot=0;++Line_num;
            for(re int k=1;k<=n;k++)
                if((p[k]-p[i])*(p[j]-p[i])==0) vis[k]=1,++tot;
            for(re int k=1;k<=n;++k)
                if((p[k]-p[i])*(p[j]-p[i])==0) f[k]=tot;
            nw+=1.0/(double)tot;
        }
        double p=1.0/(double)Line_num;
        for(re int j=1;j<=n;j++) {
            if(j==i) {pw[0].a[i][i]=nw*p;continue;}
            pw[0].a[j][i]=1.0/(double)f[j];
            pw[0].a[j][i]*=p;
        }
        for(re int j=1;j<=n;j++)f[j]=vis[j]=0;
    }
    for(re int i=1;i<=n;i++) 
        for(re int j=i+1;j<=n;j++) {
            if(ma[i][j]) continue;++num;
            for(re int k=1;k<=n;k++)
                if((p[i]-p[k])*(p[j]-p[k])==0) line[num].push_back(k);
            work();
        }
    for(re int i=1;i<=13;i++)pw[i]=pw[i-1]*pw[i-1];
    scanf("%d",&Q);int t,m;double nw;
    while(Q--) {
        scanf("%d%d",&t,&m);nw=0;
        calc(t,m-1);
        for(re int i=1;i<=num;++i) {
            double k=0;
            for(re int j=0;j<line[i].size();++j) k+=ans.d[line[i][j]];
            k*=1.0/(double)line[i].size();
            nw=max(k,nw);
        }
        printf("%.12lf\n",nw);
    }return 0;
}
//g++ cf989e.cpp -o cf989e

猜你喜欢

转载自www.cnblogs.com/asuldb/p/12170532.html