Codeforces Round #487 (Div. 2) E. A Trance of Nightfall (矩阵优化)

题意

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

存在一种操作 :

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

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

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

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

q 次询问 .

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

(n200,q200,mi104)

题解

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

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

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

dpi,j 为第 i 次到 j 号点的概率 , 我们每次询问只要回答对于每条线 最后答案就是 maxlinedpti,mi .

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

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

( 令过 P 的直线数为 s1 , 然后 Pv 直线上的点数为 s2 . 选 v 概率为 1s1×s2 )

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

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

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

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

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

fii 到当前目标 m 的概率 . 我们就可以考虑用这个矩阵转移出 ti 步后的概率 .

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

转移就是 fi=j=1ncoef[i][j] fj 就是 im 相当于 j, ijm .

注意 ti=1 的时候要特判一下 fi .

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

最后分析下时间复杂度 qwq

线上所有点个数之和为 O(n2) , 预处理是 O(n3logti) 的 , 回答单个询问做的转移次数是 O(n2logti) 的 .

所以时间复杂度就是 O(n3logti+qn2logti) . 卡卡常只要 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\n", Answer(times, ver)); } return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9174927.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(362)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示