[概率与期望][线段树与矩乘][最大子正方形][高斯消元]
求每个人被猜中一次的期望步数。
运用一些奇怪的方法。
设p[k]表示进行了k轮,每个人至少被猜中了一次的概率。
则有期望步数=sigma{k*(p[k]-p[k-1])}
因为(p[k]-p[k-1])是收敛的,在某一个k的地方它的误差会小于10^-6
所以我们只需要找到一个较大的k计算完毕即可。此题约是3*10^5。
将式子展开得K*p[K] - sigma{p[j]},其中j=[0, K-1]
由于p[k]是递增的,所以p[k]≈1,前一项可以视作K。
将K分给每一项。原式=sigma(1-p[j]) j=[0, K-1]
我们要最小化此值,就要最大化p[j].
考虑p[j]的定义,p[j]表示进行了j轮,每个人至少被猜中了一次的概率。
则有p[j] = ∏(1-(1-pro[i])^c[i]),其中pro[i]为原题中的概率,c[i]为选了这个人多少次
(1-pro[i])^c[i]表示猜了这个人c[i]次都没有猜中。用1减表示这个人猜中了。
p[j] = p[j-1] * (1-(1-pi)^ci) / (1-(1-pi)^(ci-1)),每次O(n)扫描即可。
由于(1-(1-pi)^ci) / (1-(1-pi)^(ci-1))的收敛,我们需要把它取倒数保证精度
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <cstdlib> #define maxn 1000000 using namespace std; int n, a[110]; double f[maxn], p[110], q[110]; int c[maxn]; int main(){ freopen("party.in", "r", stdin); freopen("party.out", "w", stdout); scanf("%d", &n); for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), p[i] = a[i] / 100.0, q[i] = 1; f[0] = 0; for(int dir = 1; dir <= 500000; dir ++){ double maxk = 1e12; int pos; for(int i = 1; i <= n; i ++){ double t = (1 - q[i]) / (1 - q[i] * (1 - p[i])); if(t < maxk) maxk = t, pos = i; } q[pos] *= (1 - p[pos]); f[dir] = 1; for(int i = 1; i <= n; i ++) f[dir] *= (1 - q[i]); } double ans = 0; for(int i = 0; i <= 500000; i ++) ans += 1 - f[i]; printf("%.15lf\n", ans); return 0; }
orz了一下tourist的代码QAQ
#include <bits/stdc++.h> #define mul(x, y) x * y using namespace std; typedef long long ll; const int N = 400010; int n, md; struct Matrix{ int a[2][2]; Matrix(int diag = 1){ a[0][0] = a[1][1] = diag; a[0][1] = a[1][0] = 0; } }; inline Matrix operator*(const Matrix& a, const Matrix& b){ Matrix c(0); for(int i = 0; i < 2; i ++) for(int j = 0; j < 2; j ++) c.a[i][j] = ((ll)a.a[i][0] * b.a[0][j] + (ll)a.a[i][1] * b.a[1][j]) % md; return c; } inline Matrix power(Matrix a, ll b){ Matrix res(1); while(b > 0){ if(b & 1) res = res * a; b >>= 1; a = a * a; } return res; } int s[N], value[N]; ll pos[N]; Matrix tree[N]; #define lc x<<1 #define rc x<<1|1 void build(int x, int l, int r){ if(l == r){ tree[x] = Matrix(0); tree[x].a[1][0] = 1; tree[x].a[0][1] = s[(l + n - 2) % n]; tree[x].a[1][1] = s[(l + n - 1) % n]; return; } int mid = l + r >> 1; build(lc, l, mid); build(rc, mid+1, r); tree[x] = tree[lc] * tree[rc]; } Matrix get(int x, int l, int r, int L, int R){ if(r < L || R < l || L > R) return Matrix(1); if(l == L && r == R) return tree[x]; int mid = l + r >> 1; if(R <= mid) return get(lc, l, mid, L, R); if(L > mid) return get(rc, mid+1, r, L, R); return get(lc, l, mid, L, mid) * get(rc, mid+1, r, mid+1, R); } map<long long, int> mp; inline void proceed(Matrix& res, ll pos){ Matrix other(0); other.a[1][0] = 1; if(mp.find(pos-2) == mp.end()) other.a[0][1] = s[(pos - 2 + n) % n]; else other.a[0][1] = mp[pos - 2]; if(mp.find(pos-1) == mp.end()) other.a[1][1] = s[(pos - 1 + n) % n]; else other.a[1][1] = mp[pos - 1]; res = res * other; } int main(){ ll k; scanf("%lld%d", &k, &md); if(md == 1){printf("%d\n", 0); return 0;} if(k == 0){printf("%d\n", 0); return 0;} if(k == 1){printf("%d\n", 1 % md); return 0;} scanf("%d", &n); for(int i = 0; i < n; i ++){ scanf("%d", s + i); s[i] %= md; } build(1, 0, n-1); Matrix all = tree[1]; vector<ll> special; int m; scanf("%d", &m); mp.clear(); for(int i = 0; i < m; i ++){ scanf("%lld %d", pos + i, value + i); value[i] %= md; special.push_back(pos[i] + 1); special.push_back(pos[i] + 2); mp[pos[i]] = value[i]; } special.push_back(k); sort(special.begin(), special.end()); special.resize(unique(special.begin(), special.end()) - special.begin()); while(!special.empty() && special.back() > k) special.pop_back(); Matrix res(1); ll cur = 1; for(int id = 0; id < (int)special.size(); id ++){ ll nxt = special[id], from = cur + 1, to = nxt - 1; if(from <= to){ if(from / n == to / n){ res = res * get(1, 0, n-1, from % n, to % n); } else { res = res * get(1, 0, n-1, from % n, n - 1); res = res * power(all, to / n - from / n - 1); res = res * get(1, 0, n-1, 0, to % n); } } proceed(res, nxt); cur = nxt; } printf("%d\n", res.a[1][1] % md); return 0; }
C. Codeforces Round #274 (Div. 1) E题
先考虑这道题目的简单版本,求一张图内的最大子正方形。
dp即可。f[i][j] = Min(f[i-1][j-1], f[i-1][j], f[i][j-1]) + 1;
Ex:子矩形?扫出每个点向上扩展的最大距离。单调栈。
#include <bits/stdc++.h> #define maxn 2010 using namespace std; int n, m; char s[maxn]; int f[maxn][maxn]; inline int Min(int a, int b, int c){ if(a > b) a = b; if(a > c) a = c; return a; } int main(){ scanf("%d%d", &n, &m); int ans = 0; for(int i = 1; i <= n; i ++){ scanf("%s", s+1); for(int j = 1; j <= m; j ++){ if(s[j] == '.') f[i][j] = Min(f[i-1][j-1], f[i-1][j], f[i][j-1]) + 1; ans = max(ans, f[i][j]); } } printf("%d\n", ans); return 0; } /* Input 7 8 ....X... X.....X. ........ ........ .X...... ...X.... ........ Output 4 */
现在要求更改后的最大子正方形。时光倒流使操作变为合并。维护并查集。考虑对每一行建一个并查集。对每一个点维护最左边最右边最远可以延伸到哪里。然后每次计算当前行向外扩的前缀最小值,不断更新答案
#include <bits/stdc++.h> using namespace std; #define maxn 2010 int n, m, k, ans[maxn]; bool mat[maxn][maxn]; int X[maxn], Y[maxn]; int f[maxn][maxn]; struct Ufs{ int fa[maxn]; Ufs(){for(int i = 1; i < maxn; i ++)fa[i] = i;} int getfa(int x){return x == fa[x] ? x : fa[x] = getfa(fa[x]);} }l[maxn], r[maxn]; int gl[maxn], gr[maxn]; char s[maxn]; int main(){ scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= n; i ++){ scanf("%s", s+1); for(int j = 1; j <= m; j ++) mat[i][j] = s[j] == 'X'; } for(int i = 1; i <= k; i ++) scanf("%d%d", &X[i], &Y[i]), mat[X[i]][Y[i]] = 1; int cur = 0; for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++) if(!mat[i][j]){ l[i].fa[j] = j-1; r[i].fa[j] = j+1; f[i][j] = min(f[i-1][j-1], min(f[i][j-1], f[i-1][j])) + 1; cur = max(cur, f[i][j]); } } for(int i = k; i; i --){ ans[i] = cur; int x = X[i], y = Y[i]; mat[x][y] = 0; gl[x] = l[x].fa[y] = l[x].getfa(y-1); gr[x] = r[x].fa[y] = r[x].getfa(y+1); for(int j = x-1; j; j --){ gl[j] = max(gl[j+1], l[j].getfa(y)); gr[j] = min(gr[j+1], r[j].getfa(y)); } for(int j = x+1; j <= n; j ++){ gl[j] = max(gl[j-1], l[j].getfa(y)); gr[j] = min(gr[j-1], r[j].getfa(y)); } for(int j = 1; j <= x; j ++) while(j + cur <= n && min(gr[j], gr[j+cur]) - max(gl[j], gl[j+cur]) - 1 > cur) cur ++; } for(int i = 1; i <= k; i ++) printf("%d\n", ans[i]); return 0; }
D.Ant(pas/c/cpp)
在一个奇怪的n*m的平面上有一只蚂蚁,蚂蚁一开始在(0,0)这个位置。这个平面的奇怪之处在于,从(n-1,i)这个点向右走,就会到达(0,i),从(i,m-1)向上走,就会到达(i,0)。
这只蚂蚁每一步会随机地向上或者向右走一格,直到它到达(x,y),求蚂蚁走过的期望步数。
对于10%的数据,n,m<=3
对于40%的数据,n,m<=10
对于100%的数据,n,m<=100
只会做40%的数据。把每一个格子看成一个点,点(x, y)的期望步数为0,其他暴力列方程高斯消元即可。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define maxn 110 using namespace std; double a[maxn][maxn]; void Gauss(int n){ for(int i = 1; i <= n; i ++){ for(int j = i; j <= n; j ++){ if(a[j][i]){ for(int k = i; k <= n+1; k ++) swap(a[j][k], a[i][k]); for(int k = i+1; k <= n+1; k ++) a[i][k] /= a[i][i]; a[i][i] = 1; break; } } if(a[i][i] == 0)continue; for(int j = 1; j <= n; j ++){ if(!a[j][i] || j == i)continue; double t = a[j][i] / a[i][i]; for(int k = i; k <= n+1; k ++) a[j][k] -= a[i][k] * t; } } } int n, m, x, y; #define id(i, j) (i-1) * m + j int main(){ freopen("ant.in", "r", stdin); freopen("ant.out", "w", stdout); scanf("%d%d%d%d", &n, &m, &x, &y); x ++, y ++; int N = n * m + 1, p; for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++){ int now = id(i, j); a[now][now] = 1; if(i == x && j == y)continue; p = i+1; if(p == n+1) p = 1; a[now][id(p, j)] = -0.5; p = j+1; if(p == m+1) p = 1; a[now][id(i, p)] = -0.5; a[now][N] = 1; } } Gauss(N-1); printf("%.12lf\n", a[id(1, 1)][N]); return 0; }
对于n,m<=10的数据,高斯消元即可。
不妨考虑如何从(x,y)走回(0,0)。需要注意到,如果把(i,0),(0,i)一共n+m-1个格子当做未知数,那么剩下的格子就不可能走出环了,所以剩下的格子可以直接用n+m-1个未知数线性表示出来。所以未知数的个数就减少到了O(n+m),然后高斯消元即可
#include <bits/stdc++.h> #define maxn 210 using namespace std; int n, m, x, y; double a[maxn][maxn]; void Gauss(int n){ for(int i = 1; i <= n; i ++){ for(int j = i; j <= n; j ++){ if(a[j][i]){ for(int k = i; k <= n+1; k ++) swap(a[i][k], a[j][k]); for(int k = i+1; k <= n+1; k ++) a[i][k] /= a[i][i]; a[i][i] = 1; break; } } if(a[i][i] == 0)continue; for(int j = 1; j <= n; j ++){ if (j == i)continue; double t = a[j][i] / a[i][i]; for(int k = i; k <= n+1; k ++) a[j][k] -= a[i][k] * t; } } } int id[maxn][maxn]; double K[101][101][maxn]; int main(){ freopen("ant.in", "r", stdin); freopen("ant.out", "w", stdout); scanf("%d%d%d%d", &n, &m, &x, &y); int N = n + m - 1; for(int i = 1; i < n; i ++) K[i][0][i] = 1; for(int i = 1; i < m; i ++) K[0][i][i+n-1] = 1; for(int i = 1; i < n; i ++) for(int j = 1; j < m; j ++){ for(int k = 1; k <= N; k ++) K[i][j][k] = 0.5 * (K[i-1][j][k] + K[i][j-1][k]); K[i][j][N] ++; } for(int i = 1; i < n; i ++){ int u = i; for(int k = 1; k < N; k ++) a[u][k] = 0.5 * K[i][m-1][k]; a[u][N] = -1 - 0.5 * K[i][m-1][N]; if(i-1)a[u][u-1] += 0.5; a[u][u] --; } for(int i = 1; i < m; i ++){ int u = i + n - 1; for(int k = 1; k < N; k ++) a[u][k] = 0.5 * K[n-1][i][k]; a[u][N] = - 1 - 0.5 * K[n-1][i][N]; if(i-1)a[u][u-1] += 0.5; a[u][u] --; } Gauss(n + m - 2); a[N][N] = 1; double ret = 0; for(int i = 1; i <= N; i ++) ret += K[x][y][i] * a[i][N]; printf("%.15lf\n", ret); return 0; }