• 【CF989E】A Trance of Nightfall


    题目

    根据题意,只有刚开始所在的点可能不是给定的(n)个点,之后所有的位置一定都在给定的点集中了;我们大力求一下(P_{i,j})表示从点(i)移动到点(j)的概率,这个随便暴力一下就好了,由于询问的终点是给定的,于是我们利用求出来的(P_{i,j})建一张反图

    接下来不妨考虑一下起始点的位置,我们大力猜想这个起始点不可能是两条直线的交点,感性理解一下发现这个结论非常正确;在交点上相当于对各条直线上的答案取了一个加权平均值,这个平均值一定小于答案最大的那条直线,我们把起始点放在那条直线上一定比在交点上更优。

    于是起始点只可能有两种情况,可能是给定点集中的某个点,也可能在给定点集形成的一条直线上且不是任意两条直线的交点;不难发现第二种情况包含第一种情况,我们只需要考虑第二种情况即可。

    由于直线条数不超过(O(n^2))条,于是我们可以暴力每一条直线,并处理出每一条直线上点有哪些。一条直线的答案就是上面每一个点到(t)的概率除以直线上点的个数。

    由于我们建了反图,所以转化成了(t)到上面每一个点的概率,我们现在要求的就是这个东西了;不难发现这是一个简单dp,我们可以通过矩阵来优化这个dp,但是直接上是(O(qn^3log m))的,这看起来不是很科学。

    这就是一个简单的套路了,两个矩阵相乘是(O(n^3))的,但是向量乘矩阵却是(O(n^2))的,我们求出最终的转移矩阵也只是为了和一个向量相乘,所以我们只保留(2)的次幂的矩阵,拿着向量去乘这些矩阵就好了,这样一组询问的复杂度就只有(O(n^2log m))了。

    复杂度是(O(n^3+qn^2log 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
    ",nw);
    	}return 0;
    }
    //g++ cf989e.cpp -o cf989e
    
  • 相关阅读:
    阶段3练习2
    计数器练习14
    emqx_management与http API之间的关系
    iframe框架
    html语义化标签
    htmlmeta渲染内容作用
    表单的数据传输
    css针对图片某一个位置进行点击跳转
    htmlinput控件的使用以及美化扩展部分
    link的使用(引入)
  • 原文地址:https://www.cnblogs.com/asuldb/p/12170532.html
Copyright © 2020-2023  润新知