Description
平面直角坐标系中
有n个景点,坐标分别为(xi,yi)
你一开始可以选择任意一个一个点(不一定是景点),开始移动
每次移动按照下面的顺序操作:
1、 选择一条直线,要求直线经过现在的位置且这条直线包含至少两个景点,如果有多条直线满足要求,等概率选择一条。
2、 在选择的这条直线中,等概率选择一个直线覆盖了的景点移动过去,如果目前在景点上,也有可能停住不动。
若干次询问,第i次询问从一个你选的任意点出发,然后连续移动mi步,最后到达ti的最大概率是多少。
时限2s
Input
第一行一个整数n。
接下来n行每行两个整数,表示xi,yi。
接下来一行一个整数m,表示询问数。
接下来m行每行两个整数ti,mi,表示询问。
Output
对于每个询问输出一行,表示最大的概率,与标准答案误差在10^-6以内就算正确。
Sample Input
5
0 0
1 3
2 2
3 1
4 4
10
1 1
2 1
3 1
4 1
5 1
3 2
3 3
3 4
3 5
3 6
Sample Output
0.50000000000000000000
0.50000000000000000000
0.33333333333333331483
0.50000000000000000000
0.50000000000000000000
0.18518518518518517491
0.15226337448559670862
0.14494741655235482414
0.14332164812274550414
0.14296036624949901017
Data Constraint
n<=200,m<=10000
Solution
这题卡了我许久。。。接着寻找到一个极优的复杂度,然后T掉了
预处理tr[x][y]tr[x][y]tr[x][y]表示从景点x一步走到y的概率,f[x][k][y]f[x][k][y]f[x][k][y]表示从x走k步到y的概率,f可以由tr去推
设ans[k][x]ans[k][x]ans[k][x]表示走k步到x的最大概率
因为初始点可以为无限远,那么如果你要从一条直线开始走,必然能够选择一个位置使得它不走到其他直线,如果能走多条直线,显然不会比只走一条直线优,枚举一条直线,算每个点走k-1步到x的答案,取最大值
每个询问可以O(1)求。
但预处理复杂度O(mn3)O(mn^3)O(mn3)?不!可以发现矩阵trtrtr每一行的和为1,基于此,其实f[x]f[x]f[x]是随着k渐渐收敛的,也就是说到了某个kf[x][k]f[x][k]f[x][k]和f[x][k−1]f[x][k-1]f[x][k−1]在精度范围内可视作无差别,这个k不会超过30(但之前的题反映说平均下来是12,但实际很奇妙),这样一来只用存下30次以内的矩阵,时间复杂度O(n3+q)O(n^3+q)O(n3+q)
然而题解复杂度是O(n3logn+qn2logn)O(n3\log n+qn^2log^n)O(n3logn+qn2logn)
哈哈哈吊打题解!
真香?
TLE了?
发现预处理时间太长,30远大于log好吗。。。
枚举边数再枚举上面的点其实就是枚举两两点,但这比n2n^2n2小多得多
但是,惊奇的发现有一部分是完全没用的,就是预处理答案,答案完全可以询问的时候再O(n2)O(n^2)O(n2)枚举,这样就能够过去了。
但是,那个思路只要n小一点,能将k开到1e18,m开到1e6,明显比题解优秀
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i(a),_E_(b);i<=_E_;++i)
#define REP(i,a,b) for(int i(a),_E_(b);i<_E_;++i)
#define fd(i,a,b) for(int i(a),_E_(b);i>=_E_;--i)
#define pu putchar
#define ge getchar
#define ll long long
#define db double
#define ld long double
#define N 210
using namespace std;
void read(int &n){
n=0;int s=1;char c;for(;(c=ge())>'9'||c<'0';)if(c=='-')s=-1;
for(;c>='0'&&c<='9';c=ge())n=(n<<1)+(n<<3)+c-48;n*=s;
}
void write(int n){
if(!n)pu(48);int t=0,b[20];
for(;n;n/=10)b[++t]=n%10;while(t)pu(b[t--]+48);
}
db f[N][N],s[N][30][N];
int lim[N],n,x[N],y[N],q,t,num[N*N],d[N*N][N];
bool vis[N],app[N*N][N];
int cro(int a,int b,int c,int d){return a*d-b*c;}
bool line(int a,int b,int c){
return cro(x[a]-x[b],y[a]-y[b],x[a]-x[c],y[a]-y[c])==0;
}
int main(){
scanf("%d",&n);
fo(i,1,n)scanf("%d %d",x+i,y+i);
fo(i,1,n){
memset(vis,0,sizeof vis);
int tot=0;
fo(j,1,n)if(i!=j && !vis[j]){
++tot;
fo(k,1,n)if(line(i,j,k))vis[k]=1;
}
memset(vis,0,sizeof vis);
fo(j,1,n)if(i!=j && !vis[j]){
int cnt=0;++t;int p=i>j;d[t][0]=0;
fo(k,1,n)if(line(i,j,k)){
vis[k]=1,++cnt,app[t][k]=1,d[t][++d[t][0]]=k;
if(k!=j && k!=i)if(k<j)p=1;
}num[t]=cnt;
fo(k,1,d[t][0])f[i][d[t][k]]+=1.0/(db)tot/(db)cnt;
if(p)--t;
}
}
fo(i,1,n){
s[i][0][i]=1;lim[i]=0;
for(;;){
int t=++lim[i];
fo(j,1,n)fo(k,1,n)s[i][t][k]+=s[i][t-1][j]*f[j][k];
int pd=0;
fo(j,1,n)if(fabs(s[i][t][j]-s[i][t-1][j])>1e-6)pd=1;
if(!pd)break;
}--lim[i];
}
scanf("%d",&q);
fo(i,1,q){
int x,m;scanf("%d %d",&x,&m);
--m;db ans=0;
fo(j,1,t){
db sum=0;
fo(k,1,d[j][0])sum+=s[d[j][k]][min(lim[d[j][k]],m)][x];
ans=max(ans,sum/(db)num[j]);
}
printf("%.18lf\n",ans);
}
return 0;
}
小结
答题的时候要结合数据,不要感觉比题解优就直接打,因为这么打就多了不必要的时间消耗。