zoukankan      html  css  js  c++  java
  • Codeforces Round #487 (Div. 2) E. A Trance of Nightfall (矩阵优化)

    题意

    有一个平面 , 给你 (n) 个点构成一个点集 (S) , 一开始可以选择一个平面上任意点 (P) .

    存在一种操作 :

    1 选择一条至少 通过 (S) 中任意两个点以及 (P) 的直线, 然后可以在这条直线上等概率选择一个在 (S) 中的点 (v) .

    如果有多条直线 , 那么等概率选择任意一条 . (可以原地不动)

    2 选择了这个点 (v) 后 , 将 (P) 移动到这个点 .

    也就是只有第一次可以自己随机选择点 , 后面等概率随机移动 (P)

    (q) 次询问 .

    每次有两个参数 (t_i, m_i) , 询问 在 (m_i) 次操作后 , 停留在第 (t_i) 个点的最大概率 .

    ((n le 200, q le 200, m_i le 10^4))

    题解

    考试的时候只差一点点就想出来了 ... 询问的时候每次复杂度多了一个 (O(n)) TAT

    如果第一次选的不是存在于原图中线上的点 , 那么概率是 (0) 就没有用啦 .

    所以第一次就是直接选择一条直线 , 然后去用 dp 算答案啦 qwq

    (dp_{i,j}) 为第 (i) 次到 (j) 号点的概率 , 我们每次询问只要回答对于每条线 最后答案就是 (displaystyle max_{line} dp_{t_i,m_i}) .

    转移的话 我们只要预处理出过点 (P) 的线 , 以及线上的点就行了 (用叉积判断三点共线就行了)

    然后 (P) 到这些点的转移系数就直接算就行了 qwq

    ( 令过 (P) 的直线数为 (s_1) , 然后 (Pv) 直线上的点数为 (s_2) . 选 (v) 概率为 (displaystyle frac{1}{s_1 imes s_2}) )

    暴力实现这个过程的复杂度就是 (O(qn^2m_i)) 无法承受 , 就算是预处理也需要 (O(n^2m_i)) 的空间和时间 .

    我们不难发现 , 对于这种 常系数齐次线性递推式 常常有一个套路 矩阵乘法优化 qwq .

    我们对于那些系数构造出矩阵 , 每次操作相当于一次矩阵乘法 .

    也就是说 对于矩阵中的 ((i,j)) 这个位置的值 代表 (i o j) 的概率 .

    然后 用套路 开始预处理 (2^i) 次方的转移矩阵就行了 . 每次查询的时候考虑用这些矩阵来转移出最后的答案 .

    (f_i)(i) 到当前目标 (m) 的概率 . 我们就可以考虑用这个矩阵转移出 (t_i) 步后的概率 .

    (t_i) 二进制分解后 第 (j) 位如果为 (1) 那么我们就可以用这个矩阵来转移啦 .

    转移就是 (displaystyle f_i = sum_{j=1}^{n} coef[i][j] ~ f_j) 就是 (i o m) 相当于 (forall j, ~ i o j o m) .

    注意 (t_i = 1) 的时候要特判一下 (f_i) .

    然后每次枚举一条边 , 算出线上所有点 (p_i) 的和 除以选择线上的点数 , 最后取一个 (max) 就行了 .

    最后分析下时间复杂度 qwq

    线上所有点个数之和为 (O(n^2)) , 预处理是 (O(n^3 log t_i)) 的 , 回答单个询问做的转移次数是 (O(n^2 log t_i)) 的 .

    所以时间复杂度就是 (O(n^3 log t_i+qn^2 log t_i)) . 卡卡常只要 (202ms) 就跑完啦 ~

    代码

    #include <bits/stdc++.h>zjp
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define debug(x) cout << #x << ':' << x << endl
    #define pb push_back
    using namespace std;
    
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
        return x * fh;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("E.in", "r", stdin);
    	freopen ("E.out", "w", stdout);
    #endif
    }
    
    const int N = 210;
    const double eps = 1e-8;
    
    int n, m;
    
    struct Matrix { double a[N][N]; Matrix() { Set(a, 0); } } ;
    
    inline Matrix operator * (Matrix a, Matrix b) {
    	Matrix res;
    	For (i, 1, n) For (k, 1, n) if (a.a[i][k] > eps)
    		For (j, 1, n) res.a[i][j] += a.a[i][k] * b.a[k][j];
    	return res;
    }
    
    Matrix Bas[21];
    
    
    int cnt = 0; vector<int> lis[N * N]; bool InLine[N][N];
    
    struct Point { int x, y; } lt[N];
    inline bool Collinear (Point a, Point b, Point c) {
    	return (a.y - b.y) * (b.x - c.x) == (b.y - c.y) * (a.x - b.x);
    }
    
    int tot[N]; double coef[N][N];
    
    void Init() {
    	For (i, 1, n - 1) {
    		For (j, i + 1, n) if (!InLine[i][j]) {
    			lis[++ cnt].pb(i); lis[cnt].pb(j);
    			For (k, j + 1, n)
    				if (Collinear(lt[i], lt[j], lt[k])) lis[cnt].pb(k);
    			For (k, 0, lis[cnt].size() - 1) For (l, 0, lis[cnt].size() - 1)
    				InLine[lis[cnt][k]][lis[cnt][l]] = true;
    		}
    	}
    
    	For (i, 1, cnt)
    		for (int ver : lis[i]) ++ tot[ver];
    
    	For (i, 1, cnt) {
    		int Size = (int)lis[i].size();
    		for (int beg : lis[i]) for (int ver : lis[i]) 
    			coef[beg][ver] += 1.0 / tot[beg] / Size;
    	}
    
    	For (i, 1, n) For (j, 1, n) Bas[0].a[i][j] = coef[i][j];
    
    	int cur = 1;
    	for (int i = 1; ; ++ i) {
    		if ((cur <<= 1) > 10000) break;
    		Bas[i] = Bas[i - 1] * Bas[i - 1];
    	}
    }
    
    double prob[N], tmp[N];
    
    double Answer(int times, int ver) {
    	if (!(-- times)) {
    		For (i, 1, n) prob[i] = (i == ver) ? 1 : 0;
    	}
    	else {
    		bool fir = true;
    		For (p, 0, 18)
    			if ((times >> p) & 1) {
    				if (fir) { fir = false; For (i, 1, n) prob[i] = Bas[p].a[i][ver]; }
    				else {
    					For (i, 1, n) {
    						double cur = .0; For (j, 1, n) cur += Bas[p].a[i][j] * prob[j]; tmp[i] = cur;
    					}
    					swap(prob, tmp);
    				}
    			}
    	}
    
    	double res = .0;
    	For (i, 1, cnt) {
    		double sum = .0;
    		for (int v : lis[i])
    			sum += prob[v] / (double)lis[i].size();
    		res = max(res, sum);
    	}
    	return res;
    }
    
    int main () {
    	File();
    	n = read();	
    	For (i, 1, n)
    		lt[i] = (Point) {read(), read()};
    	Init();
    
    	m = read();
    	For (i, 1, m) {
    		int ver = read(), times = read();
    		printf ("%.10lf
    ", Answer(times, ver));
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    python中类(class)和实例(instance)
    python面向对象
    python中初始化实例属性
    python之使用__future__(解决版本不同,不兼容问题)
    python中动态导入模块
    python之导入模块
    python中偏函数
    python中完善decorator
    python中编写带参数decorator
    python中编写无参数decorator
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9174927.html
Copyright © 2011-2022 走看看