博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Codeforces Round #487 (Div. 2) E. A Trance of Nightfall (矩阵优化)
阅读量:4320 次
发布时间:2019-06-06

本文共 4765 字,大约阅读时间需要 15 分钟。

题意

有一个平面 , 给你 \(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 \times s_2}\) )

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

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

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

也就是说 对于矩阵中的 \((i,j)\) 这个位置的值 代表 \(i \to 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 \to m\) 相当于 \(\forall j, ~ i \to j \to 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 
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_backusing 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
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\n", Answer(times, ver)); } return 0;}

转载于:https://www.cnblogs.com/zjp-shadow/p/9174927.html

你可能感兴趣的文章
Flash:DisplayObject的transform/matrix的潜规则、小bug
查看>>
方维系统常用的jquery库以及各个库的含义
查看>>
[LeetCode]101. Symmetric Tree
查看>>
Node.js的适用场景
查看>>
MongoDB 3.4 高可用集群搭建(二)replica set 副本集
查看>>
一个一线城市的IT白领的生活成本:3万/年
查看>>
ubuntu12.04 使用Adobe Reader PDF
查看>>
吃货联盟订餐系统(二)
查看>>
MessageBox 用法
查看>>
Developing school contest 2
查看>>
本文来自CSDN博客 map
查看>>
python 字符串中替换字符
查看>>
mysql命令行编辑模式
查看>>
《实践与思考》系列连载(6)——IT从业人员工作环境及状态调查 抽奖结果公布...
查看>>
hihocoder 1643 Puzzle Game(北京icpc2017 现场赛)
查看>>
vim 简单理解三种模式 粗暴入门
查看>>
django模板层之静态文件引入优化
查看>>
转载使用命令wsimport构建WebService客户端
查看>>
java实现23种设计模式之模版方法模式
查看>>
小程序·云开发实战 - 校园约拍小程序
查看>>