[概率与期望][线段树与矩乘][最大子正方形][高斯消元]
求每个人被猜中一次的期望步数。
运用一些奇怪的方法。
设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;
}

浙公网安备 33010602011771号