[概率与期望][线段树与矩乘][最大子正方形][高斯消元]

A.AIM Tech Round (Div. 1)D题

求每个人被猜中一次的期望步数。

 

运用一些奇怪的方法。

设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;
}

B.[Codeforces 575A]

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;
}

  

posted @ 2016-03-31 19:00  _Horizon  阅读(246)  评论(0编辑  收藏  举报